perm filename FTPSRV.MAC[IP,SYS]1 blob sn#693178 filedate 1982-12-31 generic text, type T, neo UTF8
	TITLE	FTPSRV -- FILE TRANSFER PROTOCOL SERVER
	SUBTTL	E.A.TAFT/EW13/EAT/DB33/CFE/drp--  may 80 [96bit]

	TWOSEG
	RELOC	400000

	SEARCH	C,TULIP,IMP	;ACCESS GENERAL PARAMETERS AND IMP STUFF

	VERSION	6,,43,6

; note on IO: all IO to the pty is done via the standard OFile.
; IO to the IMP connection is USUALLY done using the Error UUOs
; (EWsix and EDisix).  it you find it nessecary to change the
; the OFile (via "FoSel ImpObl", for example), make sure to change
; the OFile back when you're done, as the rest of the program
; expects it to go to the PTY.


;[96bit] first, define all the site specific things.


;[96bit] the PPn string that must be passed to login to get
;	 the free login for ftp transfers.  leave undefined if
;	 you do not wish to support free logins
Define FtpLogin<SixPPn(70,70)>	;[96bit] avsail uses 70,70

;[96bit] the octal PPn that FtpSrv should ChgPPN to before trying
;	 to login the free subjob for an Ftp transfer.  leave
;	 undefined if you do not wish the current PPN to be changed.
FtpPPn== 70 ,, 70		;[96bit] avsail uses 70,70

;[96bit] now mail information

;[96bit] define the command that should be issued to the monitor to
;	 accomplish a MLFL (Mail File) command.  The input file must
;	 be "Data:".  the line MUST end with number sign ("#") which
;	 produces a <CRLF>, followed by an exclamation mark ("!")
;	 which represents the end of the sixbit string.  Each percent
;	 sign ("%") in the string causes each successive macro
;	 statement to be executed at that point in the printing of
;	 the string.  for more detailed information, read the
;	 tulip modules.
;	 there is no default.  leave this undefined ONLY if you do
;	 not wish to support the MLFL command.
Define MlFlCommand
<
	Disix	[[SIXBIT\Mail %/IDENTI:%/FILE:DATA:#!\]
		PUSHJ	P,IMPPTY
		PUSHJ	P,HSTPRT]
>

;[96bit] define the MAIL command.  all the notes for the MLFL command
;	 apply here as well, except that this command MUST be defined.
Define MailCommand
<
	Disix	[[SIXBIT\Mail %/IDENTI:%/FILE:TTY:#!\]
		PUSHJ	P,IMPPTY
		PUSHJ	P,HSTPRT]
>

;[96bit] the PPN string that should be passed to login to get the
;	 subjob logged in for MLFL transfers.  Leave undefined if
;	 if MLFL transfers should login in the same way as ftp.
;	 (including the ChgPPn used for ftp.)  if defined, the job
;	 is logged out as soon as the transfer is completed.
;[avsail]Define MailLogin<SixPPN(N900AR0M)>	;[96bit] cmu uses Arpanet.Mail

;[96bit] the octal PPN that FtpSrv should ChgPPN to before trying
;	 to login the subjob for an MLFL transfer.  leave undefined
;	 if you do not wish the current PPN to be changed for mail.
;	 (this is ignored if MailLogin is undefined.)
MailPPn== 33125,,13776			;[96bit] cmu avoids a password


;[96bit] Define the logout routine.  leave undefined if
;	 you just want the standard "Kjob/b".
Define KjFunc
<	; CMU, of course, has to do something different.
	WSix	[SIXBIT\KJOB /F#!\];CMU- SAVE ALL FILES
	PUSHJ	P,CPYRSP	;COPY THIS
	TXNN	F,ERRFLG	;ERROR (OVER QUOTA)
	POPJ	P,		;NOPE ALL IS GOODNESS
	PUSHJ	P,CNCUSR	;STOP HIM
	WSix	[SIXBIT\CORE 0#!\];FREE ALL HIS CORE
	PJRST	PTYFLS		;AND GO AWAY
>	;end of KjFunc

;[96bit] End of site specific information


;[96bit] now clean up a little

IfDef	FtpLogin,<	$FtpLog==-1	>
IfDef	MailLogin,<	$MLogin==-1	>
ND	$FtpLog,0
ND	$MLogin,0
ND	FtpPPn,0
ND	MailPPn,0
ND	FtHarv,0	;code for harvard DIRECT
;[96bit]H=	11		;HOST TABLE INDEX FOR LOCAL HOST

;FLAGS USED IN FTPSRV
	FLAG	(OPNFLG)	;TELNET CONNECTION IS OPEN
	FLAG	(LGIFLG)	;SUBJOB IS LOGGED IN
	FLAG	(USRFLG)	;USER NAME GIVEN BUT NOT PASSWORD
	FLAG	(ERRFLG)	;ERROR MESSAGE ENCOUNTERED IN CPYRSP
	FLAG	(SLGFLG)	;FTPSRV IS A LOGGED-IN JOB
	FLAG	(PTYFLG)	;WE HAVE A PTY
	FLAG	(MAILFG)	;WE'RE IN THE MIDDLE OF A MAIL COMMAND
	FLAG	(WRPFLG)	;PTY DIALOGUE RECORDING HAS WRAPPED AROUND
;[96bit]FLAG	(LGAR0M)	;LOGGED IN AS N900AR0M
	FLAG	(TLogin)	;[96bit] should be logged out after
				;  	the command is done.
	FLAG	(MLFLFG)	;WE'RE IN THE MIDDLE OF A MLFL COMMAND
	Flag	(NlsCom)	;[96bit] processing a NLST command

;MISCELLANEOUS PARAMETERS
	PDLSIZ==100		;SIZE OF STACK
	PTY==	1		;I/O CHANNEL FOR PTY
	IMP==	2		;I/O CHANNEL FOR IMP
	ICPSKT==1		;SOCKET FOR LOCAL ICP
	TLNSKT==↑D64		;TELNET SOCKET FOR LOCAL ICP
	CMDLEN==↑D315		;MAXIMUM LEGAL FTP COMMAND LENGTH
;[CFE] Above line reflects the size of MAIL's TTY input buffer,
;[CFE]  namely about 315 characters as of 3-Jan-1981.
	WATWRN==↑D15		;TIME WE'LL WAIT BEFORE WARNING USER
	WATMAX==↑D20		;TIME WE'LL WAIT BEFORE LOGGING HIM OUT
	RECSIZ==↑D50		;NUMBER OF WORDS FOR RECORDING PTY DIALOGUE

;MACRO TO EXECUTE THE IMPUUO.  DONE AS A DEC-STYLE "CALL" SO AS TO
;   BE TRANSPORTABLE TO CMU.

DEFINE IMPUUO(AC,JUNK) <
	MCALL	AC,[SIXBIT\IMPUUO\]
>

;[96bit] Macro to define the control AC for the impuuo
Define ImpAc(Bits,Funct,Block,TimeOut<0>)
    < [ <Bits>!InSVl.(TimeOut,If.Tim)!InSVl.(Funct,If.Fnc)!<Block> ] >

;[96bit] marco to define a sixbit PPN string for the printing routines
Define SixPPn(Proj,Prog),
    < [
	ifnb <Prog>,< Sixbit \'Proj','Prog'!\ >
	ifb <Prog>,< Sixbit \'Proj'!\ >
      ]
    >
	SUBTTL	INITIALIZATION

FTPSRV:	JFCL			;IN CASE CCL ENTRY
	MOVE	P,[IOWD PDLSIZ,PDL] ;SETUP STACK
	START			;DO INITIALIZATION
	SETZM	ZEROL		;CLEAR ZEROED PART OF LOW SEGMENT
	MOVE	T1,[ZEROL,,ZEROL+1]
	BLT	T1,ZEREND-1
	MOVE	T1,[FILLH,,FILLL] ;INITIALIZE LOW SEGMENT DATA
	BLT	T1,FLLEND-1
	GETPPN	T1,		;GET OUR PPN
	  JFCL			;GETPPN SKIPS IF JACCT
	MOVEM	T1,PRJPRG	;REMEMBER IT
;[96bit] we don't care who we are anymore
;[96bit]MOVE	T1,[.IULHS,,LHOSTP] ;RETURN LOCAL HOST PARAMETERS
;[96bit]IMPUUO	T1,
;[96bit]  PUSHJ	P,Idiocy
;[96bit]HRRZ	T1,.IBHST+LHOSTP ;GET LOCAL HOST NUMBER
;[96bit]MOVSI	H,-NHOSTS	;SEARCH HOST TABLE FOR THIS NUMBER
;[96bit]HLRZ	T2,HSTTAB(H)
;[96bit]CAIE	T1,(T2)
;[96bit]AOBJN	H,.-2
;[96bit]JUMPL	H,.+2		;MAKE SURE WE FOUND ONE, AND REMEMBER INDEX
;[96bit]PUSHJ	P,Idiocy

;[96bit]MOVEI	T1,CONBLK	;SEE IF TELNET CONNECTION IS ALREADY OPEN
	Move	T1,ImpAc(If.New,.IuStt,ConBlk)		;[96bit]
	IMPUUO	T1,
	  JRST	NOTELC		;NO, GO TRY TO OPEN ONE

;HERE WITH TELNET CONNECTION OPEN TO USER
TLNOPN:	TXO	F,OPNFLG	;FLAG CONNECTION OPEN
	MOVEI	T1,IMPOBL	;DIRECT ERRORS TO THE TELNET USER
	MOVEM	T1,EFILE##
	Move	T1,.IBHST+CONBLK ;[96bit] DEFAULT HOST IS this one
	Movem	T1,HstTmp	;[96bit] put where it'll get set up
	move	T1,.IBRMT+CONBLK	; get his socket
	MOVEM	T1,RmtSkt		; and remember it for connections
	sos	t1,.IbLcl+ConBlk	; get our socket minus 1
	MOVEM	T1,LclSkt		; that's where connections go
	FSETUP	IMPIBH		;SETUP IMP I/O BLOCKS
	FSETUP	IMPOBH
	FIGET	IMPIBL		;OPEN IMP CONNECTION FOR I/O

;TYPE THE SIGNON MESSAGE
	MOVEI	T1,4		;FIVE WORDS OF MONITOR NAME
CNFGET:	MOVSI	T2,(T1)		;GET A WORD
	HRRI	T2,.GTCNF
	GETTAB	T2,
	  SETZ	T2,		;OOP......
	MOVEM	T2,SYSNAM(T1)	;STORE IT
	SOJGE	T1,CNFGET	;BACK FOR MORE
	MOVSI	T1,'300'	;OK, START WITH SIGNON MESSAGE
	EDisix	[EXP	SRVMSG
		WSIX	4,T1
		WASC	SYSNAM]
;SEARCH FOR A PTY WE CAN HAVE
	FSETUP	PTYIBH		;SETUP PTY FILE BLOCKS
	FSETUP	PTYOBH
	FoSel	PtyOBl		; start off talking naturally to pty.
	MOVX	T1,%CNPTY	;GET FIRST PTY,,# OF PTY'S
	GETTAB	T1,
NOPTAV:	  EDisix [BYEFR1,,[SIXBIT\401 N&O &PTY&S AVAILABLE.  &T&RY AGAIN LATER.#!\]]
	MOVEI	T1,(T1)		;ISOLATE NUMBER OF PTY'S

;HERE WHEN OPEN FAILS ON A PARTICULAR PTY
PTYTRY:	SOJL	T1,NOPTAV	;JUMP IF THERE AREN'T ANY MORE
	MOVEI	T2,(T1)		;GET NEXT PTY NUMBER
	SETZ	T3,		;CONVERT TO OCTAL DIGITS
	LSHC	T2,-3
	LSH	T3,-3
	TXO	T3, <'0'>B5
	JUMPN	T2,.-3
	HLRM	T3,PTYIBL+FILDEV ;STORE IN RIGHT HALF OF PTY NAME
	HLRM	T3,PTYOBL+FILDEV
	FIGET	PTYIBL		;TRY TO ASSIGN IT.  TO PTYTRY IF FAIL
	TXO	F,PTYFLG	;GOT IT -- SET FLAG
;[96bit]HRRZ	T1,HSTADR	;GET FOREIGN HOST'S ADDRESS
;[96bit]PUSHJ	P,HSTNAM##	;FIND OUT WHAT IT'S NAME IS
;[96bit]  SETZ	T1,		;ERROR, PUNT
;[96bit]MOVEM	T1,SXBHST	;STORE THE RESULTS (MAY BE ZERO)
;[96bit]MOVEM	T2,SXBHST+1
;	Pushj	P,SetNam	;[96bit] get the name, if we can.
; don't delay start up to build host tables: put this off until
; we have a command.
	MOVEI	T1,C.BYE	;GO TO BYE ROUTINE TO LOGOUT SUBJOB
	HRRM	T1,.JBREN##	;on a reenter.
	JRST	COMAND		;BEGIN PROCESSING COMMANDS
;HERE WHEN THERE IS NO TELNET CONNECTION OPEN.  IF FTPSRV IS
;   BEING RUN BY A LOGGED-IN USER, ATTEMPT TO DO AN ICP.
NOTELC:	PJOB	T1,		;GET OUR JOB NUMBER
	MOVN	T1,T1		;NEGATE FOR JOBSTS
	JOBSTS	T1,
	  PUSHJ	P,Idiocy		;SHOULDN'T FAIL
	TXNN	T1,JB.ULI	;ARE WE LOGGED IN?
	DISIX	[DOLOGO,,[SIXBIT\?L&OGIN PLEASE#.!\]]
	WSIX	[SIXBIT\P&RIVATE &FTP& SERVER RUNNING.#&M&ONITORING? !\]
	INCHRW	T1		;ASK FOR RESPONSE FROM TTY
	CAIN	T1,CR		;IF CARRIAGE RETURN
	INCHRW	T1		;  ABSORB LINE FEED
	CAIE	T1,"Y"		;YES IN EITHER UPPER OR LOWER CASE?
	CAIN	T1,"Y"+40
	TXO	F,SLGFLG	;YES, REMEMBER SERVER LOGGED-IN AND MONITORING
	pjob	t1,			; get job again
	LSH	T1,9
	ADDI	T1,ICPSKT	;BUILD LOCAL ICP SOCKET NUMBER
	DISIX	[[SIXBIT\#A&WAITING &ICP& ON SOCKET %#!\]
		WDEC	T1]
;[96bit]MOVE	T1,[7B10+<.IUREQ>B17+ICPCON] ;WAIT FOR ICP REQUEST
	Move	T1,ImpAc(If.New,.IuReq,ConBlk,7)	;[96bit]
	IMPUUO	T1,
	  JRST	ICPERR		;ERROR (MAYBE TIMED OUT)

repeat 0,<	;[tcp] old complex stuff not needed anymore.
;[96bit]HRLI	T1,.IUCON	;OK, CONNECT
	HRLI	T1,.IUCON(If.New)	;[96bit] OK, CONNECT
	IMPUUO	T1,
	  JRST	ICPERR
	MOVE	T1,.IBRMT+ICPCON ;GET HIS SOCKET (INPUT)
	ADDI	T1,3		;STORE HIS CORRECT TELNET OUTPUT SOCKET
	MOVEM	T1,.IBRMT+CONBLK
	MOVE	T1,.IBHST+ICPCON ;GET HOST NUMBER
;[96bit]HRRM	T1,.IBHST+CONBLK ;STORE IN TELNET CONNECTION BLOCK
	Movem	T1,.IBHST+CONBLK ;[96bit] STORE IN TELNET CONNECTION BLOCK
;[96bit]MOVE	T1,[.IULSN,,CONBLK] ;SET TELNET SOCKETS INTO LISTEN STATE
	Move	T1,ImpAc(If.New,.IuLsn,ConBlk)		;[96bit]
	IMPUUO	T1,
	  JRST	ICPERR
	SOS	.IBRMT+CONBLK
	AOS	.IBLCL+CONBLK
	IMPUUO	T1,
	  JRST	ICPERR
	FSETUP	ICPBLH		;OPEN ICP SOCKET FOR OUTPUT
	FOOPEN	ICPBLK
	HRRZ	T1,PRJPRG	;COMPUTE OUR FULL LOCAL SOCKET NUMBER
	LSH	T1,9
	IORI	T1,TLNSKT	;  FOR THE SERVER TELNET CONNECTION
	MOVE	T2,[POINT 8,T1,3] ;UNPACK 8 BITS AT A TIME
	ILDB	T3,T2
	WCHI	(T3)		;STUFF AN 8-BIT BYTE
	TXNE	T2,77B5		;DONE?
	JRST	.-3		;NO, DO MORE
	FOCLOS	ICPBLK		;YES, SEND ICP DATA ON ITS WAY
;[96bit]MOVE	T1,[.IUCLS,,ICPCON] ;  BY CLOSING THE ICP SOCKET
	Move	T1,ImpAc(If.New,.IuCls,ICPCon)		;[96bit]
	IMPUUO	T1,
	  JRST	ICPERR
repeat 0 continues to next page
;CONTINUATION OF ICP CODE and repeat 0

	SETZM	OFILE##		;OUTPUT BACK TO TTY
;[96bit]MOVE	T1,[IF.NWT+<.IUCON>B17+CONBLK] ;CONNECT THE TELNET SOCKETS
	Move	T1,ImpAc(If.Nwt!If.New,.IuCon,ConBlk)	;[96bit]
	IMPUUO	T1,		;DO THE OUTPUT SOCKET FIRST
	  JRST	ICPERR
	AOS	.IBRMT+CONBLK	;NOW THE INPUT SOCKET
	SOS	.IBLCL+CONBLK
;[96bit]HRLI	T1,.IUCON	;WAIT FOR THIS ONE
	HRLI	T1,.IUCON(If.New)	;[96bit] WAIT FOR THIS ONE
	IMPUUO	T1,
	  JRST	ICPERR
	SOS	.IBRMT+CONBLK	;NOW BACK TO LOOK AT THE OUTPUT SIDE
	AOS	.IBLCL+CONBLK
	IMPUUO	T1,		;WAIT FOR SOCKET TO BECOME OPEN
	  JFCL			;PROBABLY ALREADY WAS OPEN
> ;[tcp] end of repeat 0

;[96bit]MOVEI	T1,CONBLK	;GET ITS STATUS
	Move	T1,ImpAc(If.New,.IuStt,ConBlk)		;[96bit]
	IMPUUO	T1,
	  JRST	ICPERR
	LDB	T2,[POINT 6,.IBSTT+CONBLK,35] ;OUTPUT SIDE OPEN NOW?
	CAIN	T2,.ISEst		; established?
	  DISIX	[TLNOPN,,[SIXBIT\ICP &COMPLETED.#!\]]


;HERE WHEN SOMETHING FAILS DURING THE ICP.
ICPERR:	SETZM	OFILE##		;MAKE OUTPUT COME OUT ON THE TTY
	WSIX	[SIXBIT\? S&ERVER &T&ELNET &ICP& FAILED#!\]
;[96bit]MOVE	T1,[IF.NWT+<.IUCLS>B17+ICPBLK] ;CLOSE ICP BLOCK IN CASE OPEN
	Move	T1,ImpAc(If.Nwt!If.New,.IuCls,ICPBlk)	;[96bit]
	IMPUUO	T1,
	  JFCL
	JSP	T4,BYEFRC	;CLOSE CONNECTIONS IF OPEN
	SUBTTL	COMMAND TABLES

;BITS IN LH OF COMMAND DISPATCH ENTRY
	CM.LGI==1B0		;LOGIN REQUIRED FOR THIS COMMAND
	CM.HLP==1B1		;LIST COMMAND IN THE HELP MESSAGE
	CM.LGM==1B2		;[96bit] use mlfl login, and logout
				;	 the job when the transfer's
				;	 done.


DEFINE COMS <

	CC	USER,<HLP>
	CC	PASS,<HLP>
	CC	ACCT,<>
	CC	BYTE,<HLP>
	CC	SOCK,<HLP>
	CC	Pasv,<>		; give "not implemented" for this
	CC	TYPE,<HLP>
	CC	STRU,<HLP>
	CC	MODE,<HLP>
	CC	RETR,<LGI,HLP>
	CC	STOR,<LGI,HLP>
	CC	APPE,<>
	CC	RNFR,<LGI,HLP>
	CC	RNTO,<LGI,HLP>
	CC	DELE,<LGI,HLP>
	CC	LIST,<LGI,HLP>
	CC	NLst,<LGI,HLP>	;[96bit] implement name-list
	CC	ALLO,<>
	CC	REST,<>
	CC	STAT,<HLP>
	CC	ABOR,<>
	CC	BYE ,<HLP>
Ife $MLogin,<	;[96bit] MLFL doesn't need to logout
	CC	MLFL,<LGI,HLP>
>; ife $MLogin
ifn $MLogin,<	;[96bit] MLFL needs to logout
	CC	MLFL,<LGI,LGM,HLP>
>; ifn $MLogin
	CC	MAIL,<HLP>
	CC	HELP,<>
	CC	NoOp,<>		;[96bit] implement NoOp
	CC	XCWD,<LGI,HLP>
	CC	XSRC,<LGI,HLP>
	CC	XTIM,<HLP>
	CC	XREP,<>

>
;ASSEMBLE COMMAND NAMES

DEFINE CC(A,B) <
	<SIXBIT	\A\>
>

	XALL

COMTAB:	COMS

	COMLEN==.-COMTAB	;NUMBER OF COMMANDS IN TABLE
;ASSEMBLE COMMAND DISPATCH TABLE

DEFINE CC(A,B) <
	ZZ==	0
IFNB<B>,<IRP B<
	ZZ==	ZZ+CM.'B
>>
IFDEF C.'A,<
	ZZ +	C.'A
>
IFNDEF C.'A,<
	ZZ +	COMUNI
>>

COMDSP:	COMS

	SALL
	SUBTTL	FTP COMMAND DECODING AND DISPATCH

;HERE WHEN FTPSRV HAS NOTHING BETTER TO DO.  WAIT FOR INPUT FROM
;   EITHER THE IMP OR THE PTY.
COMAND:	PUSHJ	P,IMPCHK	;MAKE SURE TELNET CONNECTION IS STILL OPEN
;[tcp] there's nothing special about FTPSRV IMPs, they are just connected
;[tcp]	to TTYs, and the TTY is what we talk to.  IO.DAT cannot be on for
;[tcp]	a non-imp.
;[tcp]	STATZ	IMP,IO.DAT	;  OR MORE AVAILABLE FROM TELNET CONNECTION?
	skpinl				;[tcp] another command?
	 SKIPle	IMPIBL+FILCTR		;[tcp] perhaps read in already?
	  JRST	IMPGET		;YES, PROCESS IT
	PUSHJ	P,PTYCHK	;NO, HAS ANYTHING COME FROM THE PTY?
	  AOSA	T1,WATCNT	;NO, INCREMENT TIME WE'VE BEEN WAITING
	JRST	PTYGET		;YES, PROCESS IT
	CAIN	T1,WATWRN*↑D60	;TIME TO WARN OUR INACTIVE USER?
	EDisix	[[SIXBIT\030 Y&OU WILL BE LOGGED OFF IN % MINUTES IF YOU CONTINUE TO DO NOTHING.#!\]
		WDECI	WATMAX-WATWRN]
	CAIN	T1,WATMAX*↑D60	;TIME TO GIVE UP ON HIM?
	EDisix	[C.BYE,,[SIXBIT\430 I&NACTIVITY TIMEOUT--GOODBYE.#!\]]
	MOVEI	T1,1		;SLEEP FOR A SECOND
	SLEEP	T1,
	JRST	COMAND		;GO LOOK AGAIN


;HERE WHEN SOMETHING COMES BACK FROM THE PTY.  JUST COPY IT TO THE IMP.
PTYGET:	MOVSI	T1,'050'	;MISC MESSAGE CODE
	PUSHJ	P,CPYRSP	;COPY RESPONSE TO IMP
	JRST	COMAND		;RESUME WAITING
;HERE WHEN A MESSAGE ARRIVES FROM THE IMP.  FIRST, READ THE ENTIRE
;  LINE INTO CORE AND CHECK FOR ILLEGAL CHARACTERS AND IMPROPER TERMINATION.
IMPGET:	HLLZS	WATCNT		;RESET WAIT COUNT
;[CFE]	Clear out CmdBuf before storing into it.  Remember count of
;[CFE]	  characters stored, also; use count reading from buffer.
	setzm	CmdBuf		;[CFE] Clear first word,
	move	t1,[xwd CmdBuf,CmdBuf+1]
	blt	t1,CmdBuf+<CmdLen/5>	;[CFE]  clear the rest.
	MOVE	T1,[POINT 7,CMDBUF] ;POINT TO COMMAND STORAGE BUFFER
	MOVEM	T1,CMDPTR	;STORE FOR LATER USE
	MOVEI	T2,CMDLEN	;MAX LEGAL COMMAND LENGTH
	FISEL	IMPIBL		;INPUT FROM IMP
; IMP output uses Error UUOs.
;	FOSEL	IMPOBL		;OUTPUT POSSIBLE MESSAGES TO IMP

;MAKE SURE THIS IS A REAL MESSAGE COMING AND NOT JUST SOME LEFTOVER NULLS
IMPGE4:	RCHF	P1		;GET A CHAR FROM THE IMP
	JUMPN	P1,IMPGE1	;A REAL CHAR, PROCESS IT
;[tcp]	SKIPG	IMPIBL+FILCTR	;NO, ANY MORE INPUT DATA?
;[tcp]	STATZ	IMP,IO.DAT	;NO, MORE TO GET FROM THE IMP?
;[tcp]	JRST	IMPGE4		;YES, DO IT
	JRST	COMAND		;NO, FORGET IT

;[CFE, 3-Jan-81] If this is MAIL command input, artificially insert
;[CFE] CRLFs to break very-long lines to lengths that MAIL will
;[CFE] handle for us.
IMPGE5:	txnn	F,MAILFG	; Are we doing a MAIL?
	  jrst	IMPGE0		;  Yes: don't test here.
IMPGE7:	caig	T2,2		; More than two spaces left?
	  jrst	IMPGE6		;  No; force a CRLF.
	caig	T2,↑D15		; 15 or fewer spaces left
	 caie	P1," "		;  and this char is a space ( =40 )?
	  jrst	IMPGE0		; No, it's OK: treat ordinarily.
IMPGE6:	movei	P1,15		; Force a CRLF into cmd buffer.
	idpb	P1,T1
	movei	P1,12
	idpb	P1,T1
	subi	T2,2		; Account for spaces used.
	EWSix	[sixbit\051 L&ong &MAIL& line broken into pieces.#!\]
	jrst	CmdFin		; Send buffered text to MAIL subjob.
;[CFE] end of long-line patch

IMPGE0:	RCHF	P1		;GET A CHAR FROM THE IMP
	JUMPE	P1,IMPGE0	;IGNORE NULLS
IMPGE1:	TXNN	F,MAILFG	;MAIL MODE?
	JRST	IMPGE3		;NO, DON'T THROW OUT SPECIAL CHARS
	CAIE	P1,"C"&37	;↑C?
	CAIN	P1,"Z"&37	;OR ↑Z?
	JRST	IMPGE0		;IGNORE SINCE THEY'LL TERMINATE MAIL
;[CFE]	CAIE	P1,33		;CHECK FOR ALL ALTMODES
;[CFE]	CAIL	P1,175		;DON'T WORRY ABOUT LOSING RUBOUTS
	cain	P1,33		;[CFE] Check MAIL's <escape> terminator
	JRST	IMPGE0		;IGNORE...SAME REASON
IMPGE3:	IDPB	P1,T1		;PACK CHARACTER INTO COMMAND BUFFER
	SOJGE	T2,IMPGE2	;COUNT THE CHARACTER
	; more than we can take: load error and go die.
	Movei	T1,[SIXBIT\500 L&AST LINE WAS TOO LONG.#!\]
	JRST	CMDERR
IMPGE2:	TXNE	P2,LETTER!LGLSIX ;LEGAL CHARACTER?
;[CFE]	JRST	IMPGE0		;YES, GO ON TO NEXT
	  jrst	IMPGE5		;[CFE] Check MAIL lines, then go on.
	CAIN	P1,LF		;LINE FEED?
	JRST	CMDFIN		;YES, END OF COMMAND
	TXNE	F,MAILFG	;IN MAIL MODE?
;[CFE]	JRST	IMPGE0		;YES, STORE CHAR WITHOUT FURTHER ADO
	  JRST	IMPGE7		;[CFE] YES, STORE CHAR after size check
	CAIN	P1,CR		;CARRIAGE RETURN?
	RCHF	P1		;YES, GET NEXT
	JUMPE	P1,.-1		;IGNORE NULLS
	CAIN	P1,LF		;IS NEXT LINE FEED?
	JRST	IMPGE1		;YES, FINISH OFF THE LINE
	Movei	T1,[SIXBIT\500 L&AST LINE WAS UNRECOGNIZABLE.#!\]

;HERE WHEN THE COMMAND IS IN ERROR.  error message in T1.
CMDERR:	CAIN	P1,LF		;LINE FEED?
	JRST	CMDER1		;YES
	RCHF	P1		;NO, DISCARD AND GET NEXT
	JRST	CMDERR
CMDER1:	EWSix	(T1)		; send the error message
	JRST	COMAND		;WAIT FOR NEXT COMMAND
;HERE WHEN A COMPLETE COMMAND HAS BEEN INPUT.  DECIPHER IT
CMDFIN:				;THE MAIL FUNCTION ACCEPTS DATA OVER THE
				;TELNET CONNECTION, SO WE HAVE TO CHECK IT
;[CFE]	Set up character count first.
	subi	t2,CmdLen	;[CFE] Get negative character count
	movnm	t2,CmdCnt	;[CFE]  and store for RCHICB.
	TXNN	F,MAILFG	;IN MAIL MODE?
	JRST	CMDIS		;NO, A COMMAND IT IS
	PUSHJ	P,C.MAIX	;HANDLE THIS LINE
	JRST	COMAND		;AND TRY THE NEXT
CMDIS:	FSETUP	IMPCBH		;SETUP IMP INPUT PSEUDO-FILE
	FISEL	IMPCBL		;SELECT IT
	MOVE	T1,[POINT 6,T2]	;PREPARE TO PACK COMMAND NAME
	SETZ	T2,
CMDFN1:	RCHF	P1		;GET A CHAR
	TXNN	P2,LETTER	;IS IT A LETTER?
	JRST	CMDSRC		;NO, END OF COMMAND
	SUBI	P1,40		;CONVERT TO SIXBIT
	TXNE	T1,77B5		;IS THERE ROOM FOR MORE LETTERS?
	IDPB	P1,T1		;YES, STORE IT
	JRST	CMDFN1		;BACK FOR MORE

;HERE WHEN END OF COMMAND NAME REACHED
CMDSRC:	JUMPN	T2,CMDSR1	;JUMP IF NONBLANK
	EWSix	[SIXBIT\500 L&AST LINE WAS UNRECOGNIZABLE.#!\]
	JRST	COMAND		;WAIT FOR NEXT COMMAND
CMDSR1:	CAIE	P1," "		;WAS THE CHAR A SPACE?
	LCHF	P1		;NO, BACK UP OVER IT
	MOVEM	T2,CMDNAM	;REMEMBER COMMAND NAME
	MOVSI	T1,-COMLEN	;NUMBER OF COMMANDS
	CAME	T2,COMTAB(T1)	;SEARCH FOR COMMAND NAME
	AOBJN	T1,.-1
	JUMPGE	T1,CMDNFD	;JUMP IF NOT IN TABLE
	MOVE	P4,COMDSP(T1)	;GET CORRESPONDING DISPATCH ENTRY
	TXNE	P4,CM.LGI	;LOGIN REQUIRED?
	TXNE	F,LGIFLG	;YES, IS SUBJOB LOGGED IN?
	JRST	.+3		;YES, OR NOT REQUIRED
	PUSHJ	P,FRELGI	;NO, ATTEMPT A FREE LOGIN
	  JRST	COMAND		;UNSUCCESSFUL (MSG ALREADY PRINTED)
	Call	SetNam		;[96bit] make sure have set host up.
	PUSHJ	P,(P4)		;DO COMMAND PROCESSING
	JRST	COMAND		;WAIT FOR NEXT COMMAND


;HERE WHEN COMMAND NAME NOT FOUND
CMDNFD:	EDisix	[COMAND,,[SIXBIT\500 % &COMMAND NOT RECOGNIZED.#!\]
		WNAME	CMDNAM]

;HERE WHEN COMMAND IS NOT IMPLEMENTED
COMUNI:	EDisix	[COMAND,,[SIXBIT\506 % &COMMAND NOT IMPLEMENTED.#!\]
		WNAME	CMDNAM]
	SUBTTL	SYSTEM ACCESS COMMANDS

;   USER <USER NAME>

C.USER:	TXZE	F,LGIFLG	;IS USER ALREADY LOGGED IN?
	PUSHJ	P,LGOUSR	;YES, LOG HIM OUT FIRST
	TXOE	F,USRFLG	;USER NAME ALREADY GIVEN?
	PUSHJ	P,CNCUSR	;YES, FORCE SUBJOB TO MONITOR LEVEL
	Disix	[[SIXBIT\LOGIN %#!\] ;SEND LOGIN COMMAND TO SUBJOB
		PUSHJ	P,IMPPTY]
	PUSHJ	P,CHKLGI	;GO TAKE A LOOK AT HOW WE DID
	  PJRST	LGIERR		;DROPPED ON OUR NOSE. TELL USER
	  EDisix [PTYFLS,,[SIXBIT\330 P&ASSWORD, PLEASE.#!\]]
	PJRST	LGIFIN		;NO PSW NEEDED, WELCOME HIM
;   PASS <PASSWORD>

C.PASS:	TXNN	F,USRFLG	;GIVEN USER NAME YET?
	EDisix	[CPOPJ##,,[SIXBIT\504 USER &COMMAND MUST PRECEDE PASSWORD.#!\]]
	PUSHJ	P,IMPPTY	;OK, COPY PASSWORD TO LOGIN
	W2CHI	CRLF		;TERMINATE IT
	PUSHJ	P,GETRSP	;WAIT FOR RESPONSE
	  PJRST	LGIERR		;ERROR
	PUSHJ	P,PTYF1L	;FLUSH LINE OF ASTERISKS
	PUSHJ	P,GETRSP	;CHECK RESPONSE ON NEXT LINE
	  PJRST	LGIERR		;ERROR

;HERE WHEN LOGIN OPERATION FINISHED
LGIFIN:	TXC	F,USRFLG!LGIFLG ;CLEAR USRFLG, SET LGIFLG
	PUSHJ	P,SJBPPN	;FIND OUT THE PPN OF OUR SUBJOB
	MOVEM	T1,PRJPRG	;SAVE FOR LATER USE
	MOVSI	T1,'050'	;COPY RESPONSE TO USER AS SYSTEM INFO
	LCHF	P1		;DON'T MISS FIRST CHAR OF RESPONSE
	PUSHJ	P,CPYRSP
	EWSix	[SIXBIT\230 L&OGIN SUCCESSFUL.#!\]
	POPJ	P,



;   ACCT <ACCOUNT STRING>

C.ACCT:	EWSix	[SIXBIT\200 A&CCOUNTS NOT USED ON THIS SYSTEM.#!\]
	POPJ	P,
;   BYE

C.BYE:	move	p,[iowd PdlSiz,Pdl] ;RESET THE STACK
	txne	f,PtyFlg	;DO WE HAVE A PTY?
	  pushj	p,FreOut	;LOG possible SUBJOB OFF
	EWSix	[SIXBIT\231 B&YE.#!\] ;TRY TO BE FRIENDLY
	Releas	Pty,
	txz	f,PtyFlg	;REMEMBER WE DON'T HAVE A PTY ANY MORE
	jsp	t4,ByeFrc	; Remember how we got here

;HERE TO FORCE BYE COMMAND WHEN WE KNOW THE SUBJOB ISN'T LOGGED IN
BYEFRC:
;[CFE] First, see if the Imp connection is open; don't hang trying
;[CFE]  to send to an absent connection!  Note: this doesn't eliminate
;[CFE]  race conditions between remote-close and this RELEASE, but it
;[CFE]  does narrow the race window.
	pushj	p,ImpChk	;[CFE] One final test.  Will terminate.
	movei	t1,Imp		;[CFE] This is channel to reset, maybe.
	txnn	f,OpnFlg	;[CFE] Is conn still open?
	 ResDv.	t1,		;[CFE]  No; flush the device buffers.
	  jfcl			;[CFE] (ok, we were just trying...)
	pushj	p,ImpChk	;[CFE] Test again.
	RELEASE	IMP,		;FORCE OUT ANY PENDING MESSAGES
;[96bit]MOVE	T1,[IF.NWT+<.IUCLS>B17+CONBLK] ;CLOSE TELNET CONNECTIONS
	Move	T1,ImpAc(If.Nwt!If.New,.IuCls,ConBlk)	;[96bit]
	SETZM	CONBLK+.IBLCL	;INPUT SIDE
	IMPUUO	T1,		;NO WAIT FOR ACTION
	  JFCL
;[tcp]	AOS	CONBLK+.IBLCL	;NOW OUTPUT SIDE
;[tcp]	IMPUUO	T1,
;[tcp]	  JFCL
DOLOGO:	LOGOUT			;GO AWAY.

; Dummy BYEFRC callers for tracing where the hanging comes from.
BYEFR1:	jsp	t4,ByeFrc	; Remember PC
BYEFR2:	jsp	t4,ByeFrc
BYEFR3:	jsp	t4,ByeFrc


FREOUT:
	TXZE	F,USRFLG!MAILFG ;IF IN LOGIN OR MAIL...
	PUSHJ	P,CNCUSR	;FORCE SUBJOB TO COMMAND LEVEL
	TXZE	F,LGIFLG	;IS SUBJOB LOGGED IN?
	PUSHJ	P,LGOUSR	;YES, LOG IT OUT
	pjrst	PTYFLS		;MAKE SURE ALL OUTPUT IS ABSORBED
	SUBTTL	DATA TRANSFER PARAMETER COMMANDS

repeat 0,<	; no byte size in TCP

;   BYTE <BYTE SIZE>

C.BYTE:	PUSHJ	P,GETDEC	;GET BYTE SIZE
	  JRST	BYTERR		;ERROR IN NUMBER
	CAIE	P1,LF		;END OF LINE?
BYTERR:	EDisix	[CPOPJ##,,[SIXBIT\501 B&YTE SIZE SPECIFICATION ERROR.#!\]]
	CAIL	T1,1		;CHECK BYTE SIZE FOR LEGALITY
	CAILE	T1,↑D255
	JRST	BYTERR		;OUT OF RANGE
	CAIE	T1,↑D8		;CHECK FOR BYTE SIZES THAT OUR
	CAIN	T1,↑D36		;  CRUMMY IMPSER CAN HANDLE PROPERLY
	CAIA			;OK
	EDisix	[CPOPJ##,,[SIXBIT\506 B&YTE SIZE % NOT SUPPORTED.#!\]
		WDECI	(T1)]
	MOVEM	T1,BYTSIZ	;OK, STORE BYTE SIZE
	EDisix	[CPOPJ##,,[SIXBIT\200 B&YTE SIZE % ACCEPTED.#!\]
		WDECI	(T1)]

> ; end of repeat 0
;   SOCK <SOCKET>      OR     SOCK <HOST>,<SOCKET>

C.SOCK:	PUSHJ	P,GETDEC	;GET DECIMAL NUMBER
	  JRST	SKTERR		;ERROR
	Caie	P1,"."		;[96bit] <Host>.<Site>?
	 Cain	P1,"/"		;[96bit] or <Host>/<Site>?
	  Jrst	[		;[96bit] one of them: must be host.
		 Move	T2,T1		;[96bit] save host number
		 Pushj	p,GetDec	;[96bit] get the site number
		   Jrst	SockBH		;[96bit] no site: bad format
		 Caie	P1,","		;[96bit] now a socket?
		   Jrst	SktErr		;[96bit] no: not legal.
		 Jrst	Sockt3		;[96bit] ok: go juggle right
		]
	;[96bit] just a straight host or socket number.
	CAIE	P1,","		;COMMA?
	JRST	SOCKT1		;NO, NOT CHANGING HOST
;[96bit]CAIL	T1,1		;YES, CHECK FOR LEGAL HOST NUMBER
	CAILE	T1,↑D255	; does it look like in old format?
	  Jrst	Sockt2		;[96bit] full host: just check and store
	;[96bit] old format: convert to proper format
	LDB	T2,[Point 2,T1,35-6]	;[96bit] host number
	Andi	T1,77			;[96bit] mask out host number
Sockt3:	Dpb	T2,[Pointr (T1,Ih.Hst)]	;[96bit] host in place

Sockt2:	Txnn	T1,Ih.Imp	;[96bit] is there a site?
	  Jrst	SockBH		;[96bit] no: illegal host
	Movem	T1,HstTmp	;[96bit] save the host number
	PUSHJ	P,GETDEC	;GET SOCKET NUMBER
	  JRST	SKTERR		;ERROR
SOCKT1:	CAIN	P1,LF		;CHECK FOR LEGAL FORMAT
	TLNE	T1,(-1←↑D32)	;AND FOR LEGAL SOCKET NUMBER
	  Jrst	SktErr		; out of range
	MOVE	T2,T1		;OK, COPY SOCKET NUMBER
	ANDCAI	T2,1		;HIS INPUT IS OUR OUTPUT, SO COMPLEMENT
	MOVEM	T1,RmtSkt	;STORE NEW REMOTE INPUT OR OUTPUT SOCKET
	Call	SetNam		;[96bit] store HstTmp, and get new name.
				;	 (saves T1 & T2)
	EDisix	[CPOPJ##,,[SIXBIT\200 S&OCKET % AT HOST % (%) ACCEPTED.#!\]
		WDEC	T1
;[96bit]	WDEC	HSTADR
		Call	HstPrt	;[96bit] print host name
		Call	HstNoo	;[96bit] and print number, to make
				;	 clear how we interpreted
		]
SockBH:	EDisix	[CPOPJ##,,[SIXBIT\501 H&OST NUMBER SPECIFICATION ERROR.#!\]]
SKTERR:	Clearm	HstTmp		;[96bit] clear potential new host adr
	EWSix	[SIXBIT\501 S&OCKET NUMBER SPECIFICATION ERROR.#!\]
	Return
repeat 0,<	; con't handle odd types

;   TYPE <TYPE CODE>

C.TYPE:	PUSHJ	P,SPNOR		;IGNORE SPACES
	MOVSI	T1,-TYPLEN	;PREPARE TO SEARCH TYPE TABLE
	HLRZ	T2,TYPCOD(T1)	;GET TYPE CODE
	CAIE	T2,(P1)		;IS THIS IT?
	AOBJN	T1,.-2		;NO, TRY NEXT
	JUMPGE	T1,.+3		;JUMP IF NOT FOUND
	PUSHJ	P,SPNOR1	;OK, CHECK FOR LEGAL FORMAT
	CAIE	P1,LF
	EDisix	[CPOPJ##,,[SIXBIT\501 D&ATA TYPE SPECIFICATION ERROR.#!\]]
	MOVE	T1,TYPCOD(T1)	;FETCH TYPE DESCRIPTOR
	TRNE	T1,400000	;IMPLEMENTED?
	EDisix	[CPOPJ##,,[SIXBIT\506 T&YPE % NOT IMPLEMENTED.#!\]
		WCHI	(T2)]	;NO
	MOVEM	T1,XFRTYP	;YES, STORE NEW TYPE DESCRIPTOR
	EDisix	[CPOPJ##,,[SIXBIT\200 T&YPE % ACCEPTED.#!\]
		WCHI	(T2)]


;TYPE TABLE
TYPCOD:	"A" ,,	0		;ASCII
	"I" ,,	1		;IMAGE
	"L" ,,	-1		;LOCAL BYTE (NOT IMPLEMENTED)
	"P" ,,	-1		;PRINT FILE (NOT IMPLEMENTED)
	"E" ,,	-1		;EBCDIC PRINT FILE (NOT IMPLEMENTED)

	TYPLEN==.-TYPCOD	;NUMBER OF DIFFERENT KNOWN TYPE CODES

> ; end of repeat 0
repeat 0,<	; not implemented in TCP

;   STRU <STRUCTURE CODE>

C.STRU:	PUSHJ	P,SPNOR		;IGNORE SPACES
	MOVSI	T1,-STRLEN	;PREPARE TO SEARCH STRUCTURE TABLE
	HLRZ	T2,STRCOD(T1)	;GET AN ENTRY
	CAIE	T2,(P1)		;IS THIS IT?
	AOBJN	T1,.-2		;NO
	JUMPGE	T1,.+3		;JUMP IF NOT FOUND
	PUSHJ	P,SPNOR1	;CHECK SYNTAX
	CAIE	P1,LF		;DID EOL IMMEDIATELY FOLLOW?
	EDisix	[CPOPJ##,,[SIXBIT\501 S&TRUCTURE SPECIFICATION ERROR.#!\]]
	MOVE	T1,STRCOD(T1)	;OK, GET SPECIFIER WORD
	TRNE	T1,400000	;IS IT IMPLEMENTED?
	EDisix	[CPOPJ##,,[SIXBIT\506 S&TRUCTURE % NOT IMPLEMENTED.#!\]
		WCHI	(T2)]
	MOVEM	T1,STRTYP	;OK, STORE STRUCTURE SPECIFIER
	EDisix	[CPOPJ##,,[SIXBIT\200 S&TRUCTURE % ACCEPTED.#!\]
		WCHI	(T2)]


STRCOD:	"F" ,,	0		;FILE (NO RECORD STRUCTURES)
	"R" ,,	-1		;RECORD (NOT IMPLEMENTED)

	STRLEN==.-STRCOD

> ; end of repeat 0
repeat 0,<	; not implemented in this TCP hack

;   MODE <MODE CODE>

C.MODE:	PUSHJ	P,SPNOR		;IGNORE SPACES
	MOVSI	T1,-MODLEN	;SEARCH MODE TABLE
	HLRZ	T2,MODCOD(T1)
	CAIE	T2,(P1)		;IS THIS IT?
	AOBJN	T1,.-2		;NO, TRY NEXT
	JUMPGE	T1,.+3		;JUMP IF NOT FOUDN
	PUSHJ	P,SPNOR1	;CHECK FOR LEGAL SYNTAX
	CAIE	P1,LF
	EDisix	[CPOPJ##,,[SIXBIT\501 M&ODE SPECIFICATION ERROR.#!\]]
	MOVE	T1,MODCOD(T1)	;OK, FETCH MODE SPECIFIER
	TRNE	T1,400000	;IMPLEMENTED?
	EDisix	[CPOPJ##,,[SIXBIT\506 M&ODE % NOT IMPLEMENTED.#!\]
		WCHI	(T2)]
	MOVEM	T1,MODTYP	;OK, SAVE MODE SPECIFIER
	EDisix	[CPOPJ##,,[SIXBIT\200 M&ODE % ACCEPTED.#!\]
		WCHI	(T2)]


MODCOD:	"S" ,,	0		;STREAM
	"B" ,,	-1		;BLOCK (NOT IMPLEMENTED)
	"T" ,,	-1		;TEXT (NOT IMPLEMENTED)
	"H" ,,	-1		;HASP (NOT IMPLEMENTED)

	MODLEN==.-MODCOD

> ; end of repeat 0
	SUBTTL	FTP DATA TRANSFER FUNCTIONS

;   RETR <PATHNAME>

C.RETR:	MOVE	T1,[SIXBIT\DATA\] ;LOGICAL NAME FOR IMP DEVICE
	HRRZ	T2,XFRTYP	;DATA TYPE FOR TRANSFER
	PUSHJ	P,DoOpen	;OPEN SUBJOB'S IMP OUTPUT CONNECTION
	  POPJ	P,		;ERROR--MESSAGE ALREADY PRINTED
	WSix	[SIXBIT\R PIP#!\] ;START SUBJOB RUNNING PIP
	PUSHJ	P,GETRSP	;WAIT FOR RESPONSE
	  JRST	XFRERR		;ERROR??
	HRRZ	T1,XFRTYP	;GET TRANSFER TYPE
	Disix	[[SIXBIT\DATA: = %#!\] ;ENTER PIP COMMAND
		PUSHJ	P,IMPPTY]
RtrEnd:	;[96bit] end a RETR or LIST
	PUSHJ	P,XFRCHK	;CHECK FOR SUCCESSFUL COMPLETION
	  POPJ	P,		;ERROR, MESSAGE ALREADY PRINTED
	PUSHJ	P,CNCUSR	;FORCE SUBJOB TO COMMAND LEVEL
	WSix	[SIXBIT\IMP CLOSE DATA:#!\] ;CLOSE DATA CONNECTION
	MOVSI	T1,'452'	;CODE TO USE IF ERROR
	PUSHJ	P,GETRSP	;WAIT FOR RESPONSE
	  PJRST	CPYRSP		;ERROR--COPY MESSAGE TO USE4R AND QUIT
	EWSix	[SIXBIT\252 T&RANSFER COMPLETED.#!\]
	PJRST	PTYFLS		;FLUSH PTY OUTPUT AND RETURN
;   STOR <PATHNAME>

C.STOR:	MOVE	T1,[SIXBIT\DATA\] ;LOGICAL NAME FOR IMP DEVICE
	HRRZ	T2,XFRTYP	;DATA TYPE FOR TRANSFER
	PUSHJ	P,DoOpen	;OPEN SUBJOB'S IMP INPUT CONNECTION
	  POPJ	P,		;ERROR--MESSAGE ALREADY PRINTED
	WSix	[SIXBIT\R PIP#!\] ;START SUBJOB RUNNING PIP
	PUSHJ	P,GETRSP	;WAIT FOR RESPONSE
	  JRST	XFRERR		;COULDN'T START PIP
	HRRZ	T1,XFRTYP	;FETCH TRANSFER TYPE
	Disix	[[SIXBIT\% = DATA:#!\] ;ENTER PIP TRANSFER COMMAND
		PUSHJ	P,IMPPTY
		]
;[tcp]	PUSHJ	P,XFRCHK	;WAIT FOR SUCCESSFUL COMPLETION
;[tcp]	  POPJ	P,		;ERROR, MESSAGE ALREADY PRINTED
;[tcp]	EDisix	[CNCUSR,,[SIXBIT\252 T&RANSFER COMPLETED.#!\]]
	jrst	RtrEnd			;[tcp] standard out


ife FtHarv,<	;[96bit] harvard DIRECT does not support /InDir
;   Nlst <PathName>		[96bit]

C.Nlst:	TXO	F,NlsCom	;[96bit] remember we're doing NLST
;	Jrst	C.List		;[96bit] fall into LIST command
>	;end of IFE FtHarv


;   LIST <PATHNAME>

C.LIST:
;[96bit]WSix	[SIXBIT\ASSIGN IMP LPT#!\] ;KLUDGE TO DIRECT OUTPUT FROM
;[96bit]PUSHJ	P,GETRSP	;  HARVARD DIRECT TO AN IMP DEVICE.
;[96bit]  EDisix [PTYFLS,,[SIXBIT\454 N&O &IMP&S AVAILABLE.#!\]]
;[96bit]PUSHJ	P,PTYFLS	;FLUSH "IMPN ASSIGNED" MESSAGE
;[96bit]MOVSI	T1,'LPT'	;LOGICAL DEVICE NAME
	MOVE	T1,[Sixbit \Data\]	;[96bit] normal logical name
	MOVEI	T2,0		;ASCII DATA TYPE
	PUSHJ	P,DoOpen	;OPEN DATA CONNECTION FOR OUTPUT
	  POPJ	P,		;ERROR, MSG ALREADY PRINTED
Ife FTHarv,<	;[96bit] harvard DIRECT doesn't support /InDirect
	TXZE	F,NlsCom	;[96bit] an NLST? (Clear flag if on)
	  SKIPA	T1,[Sixbit \/Indir\]	;[96bit] yes: do indirect
	SETZ	T1,		;[96bit] LIST command: don't do indirect
;[96bit]Disix	[[SIXBIT\DIRECT /L %#!\]
	Disix	[RtrEnd,,[SIXBIT\DIRECT Data:=% %#!\]
		WNAME	T1	;[96bit] give the /I if it's there
		PUSHJ	P,IMPPTY]
>	;end of IFE FtHarv
ifn FtHarv,<	;[96bit] harvard DIRECT is "non-standard"
	Disix	[RtrEnd,,[SIXBIT\DIRECT %/FILE=Data:#!\]
		PUSHJ	P,IMPPTY]
>	;end of IFN FtHarv
;[96bit]PUSHJ	P,XFRCHK	;WAIT FOR COMPLETION OF DATA TRANSFER
;[96bit]  POPJ	P,		;ERROR--MESSAGE ALREADY PRINTED
;[96bit]PUSHJ	P,PTYFLS	;GET RID OF ANY GARBAGE FROM DIRECT
;[96bit]WSix	[SIXBIT\IMP CLOSE LPT:#!\] ;CLOSE DATA CONNECTION
;[96bit]MOVSI	T1,'452'	;ERROR CODE TO USE IF ERROR
;[96bit]PUSHJ	P,GETRSP	;WAIT FOR RESPONSE
;[96bit]  PJRST	CPYRSP		;ERROR, CPY RESPONSE TO USER
;[96bit]EWSix	[SIXBIT\252 T&RANSFER COMPLETED.#!\]
;[96bit]PJRST	PTYFLS		;FLUSH REMAINING PTY OUTPUT
;     MLFL <PPN>

IfDef MlFlCommand,<	;[96bit] if we are supporting Mail File commands
			;	 then define this, else leave undefined
			;	 and let the command macro sort it out.
C.MLFL:	MOVE	T1,[SIXBIT/DATA/];THE LOGICAL NAME WE WANT TO USE
	MOVEI	T2,0		;TRANSFER IN ASCII MODE
	PUSHJ	P,DoOpen	;TRY TO GET IMP
	  PJRST ML.ERR		;FAILED..GIVE UP
	TXO	F,MLFLFG	;SET INSIDE MLFL FLAG
;[96bit]Disix	[[SIXBIT\MAIL /ZVRF/ZXC/TO:%/IDENTI:%/FILE:DATA:#!\]
;[96bit]	PUSHJ	P,IMPPTY
;[96bit]	PUSHJ	P,HSTPRT]
	MlFlCommand		; do the right mail file command
	PUSHJ	P,XFRCHK	;WAIT TIL THINGS FINISH UP
	  PJRST ML.ERR		;SOMETHING DIED ALONG THE WAY
	MOVSI	T1,'051'	;GENERAL FTP COMMENTARY
	LCHF	P1			;GET FIRST CHAR
	PUSHJ	P,CPYRSP	;COPY ALL RESPONSES FROM MAIL
	;[96bit] assume no trouble
	Movei	T2,[SIXBIT/252 MAIL &TRANSFER COMPLETED.#!/]
	TXNE	F,ERRFLG	;ANY ERRORS IN RESPONSES?
	  Movei	T2,[SIXBIT/454 MLFL &FAILED.#!/]  ;[96bit] trouble.
	EWSix	(T2)		;[96bit] give the error message
	TXZ	F,MlFlFg	;[96bit] clear mail flag
;[96bit]TXZE	F,LGAR0M	;DID WE LOGIN AS AR0M?
	TXZE	F,TLogin	;[96bit] want to undo login?
	PJRST	FREOUT		;DO A LOGOUT AND RETURN
	PJRST	PTYFLS		;GET RID OF EXTRA PTY TRASH

ML.ERR:	TXZ	F,MLFLFG
;[96bit]TXZE	F,LGAR0M
	TXZE	F,TLogin		;[96bit] undo login?
	PUSHJ	P,FREOUT
	POPJ	P,

>	; end IfDef MlFlCommand
	SUBTTL	MISCELLANEOUS FTP FUNCTIONS

;   MAIL <PPN>

C.MAIL:
;[96bit]Disix	[[SIXBIT\MAIL /ZVRF/ZXC/TO:%/IDENTI:%/FILE:TTY:#!\]
;[96bit]	PUSHJ	P,IMPPTY
;[96bit]	PUSHJ	P,HSTPRT]
	MailCommand		;[96bit] do the right mail command
;[CFE]	MOVSI	T1,'507'	;A GENERAL ERROR CODE
	MOVSI	T1,'454'	;[CFE] A temporary-failure code.
	TXO	F,MAILFG	;[CFE] Let GETRSP make badness into
				;[CFE]   permanent-failure type codes.
	PUSHJ	P,GETRSP	;SEE HOW IT GOES
;[CFE]	  PJRST	CPYRSP		;NOT WELL
	  PJRST	[TXZ	F,MAILFG	;[CFE] Clear this state first
		 PJRST	CPYRSP]		;NOT WELL
;[CFE]	TXO	F,MAILFG	;TELL COMAND TO COME HERE FOR A WHILE
	EWSix	[SIXBIT\350 E&NTER MAIL, ENDED BY A LINE WITH JUST A '.'#!\]
	PJRST	PTYFLS		;FORGET ANYTHING ELSE MAIL SAID

C.MAIX:	MOVE	T1,CMDPTR	;HERE WHEN A TELNET LINE COMES IN WHILE IN MAIL
	ILDB	T2,T1		;SEE IF IT IS ONLY A .<CR>
	CAIE	T2,"."		;WHICH IS THE MAIL TERMINATION CHARACTER
	JRST	MAIX1		;WELL, NOT YET
	ILDB	T2,T1		;IS THE NEXT A <CR>?
	CAIE	T2,CR
	JRST	MAIX1		;NO, SEND IT ALL OFF TO THE PTY
	FOSEL	PTYOBL		;IT IS. FINISH UP MAIL
	W2CHI	<"Z"-100>B28+LF	;AND GIVE IT THE CTRL-Z IT WANTS
				;+ A LF TO FORCE OUT THE BUFFER
	PUSHJ	P,XFRCK1	;WAIT TILL THINGS FINISH UP
		POPJ	P,	;SOMETHING WENT WRONG
	MOVSI	T1,'051'	;GENERAL RESPONSE CODE
	LCHF	P1		;GET FIRST CHAR
	PUSHJ	P,CPYRSP	;COPY RESPONSES LOOKING FOR ERRORS
	;[96bit] assume no trouble
	Movei	T2,[SIXBIT/256 MAIL &COMPLETED.#!/]
	TXNE	F,ERRFLG	;ANY ERRORS IN RESPONSES?
	  Movei	T2,[SIXBIT/454 MAIL &FAILED.#!/]  ;[96bit] trouble.
	EWSix	(T2)		;[96bit] give the error message
	TXZ	F,MAILFG	;CLEAR THIS
	PJRST	PTYFLS		;THROW OUT ANY GARBAGE

MAIX1:	Disix	[[SIXBIT\%#!\]
		PUSHJ	P,IMPPTY]
	POPJ	P,		;FINISHED THIS LINE, TRY ANOTHER
;   DELE <PATHNAME>

C.DELE:	Disix	[[SIXBIT\DELETE %#!\]
		PUSHJ	P,IMPPTY]
	MOVSI	T1,'501'	;ONLY POSSIBLE ERROR IS SYNTAX ERROR
	PUSHJ	P,GETRSP	;WAIT FOR RESPONSE
	  PJRST	CPYRSP		;ERROR, PRINT MESSAGE
	LCHF	P1		;BACK OVER FIRST CHAR OF RESPONSE
	MOVSI	T1,'050'	;GENERAL FTP COMMENTARY
	PUSHJ	P,CPYRSP	;COPY DELETE RESPONSE TO USER
	TXNN	F,ERRFLG	;WERE THERE ANY ERRORS?
	EDisix	[CPOPJ##,,[SIXBIT\254 D&ELETE COMPLETED.#!\]]
	EDisix	[CPOPJ##,,[SIXBIT\451 D&ELETE UNSUCCESSFUL.#!\]]



;   ALLO <DECIMAL INTEGER>

C.ALLO:	EWSix	[SIXBIT\200 A&LLOCATION NOT REQUIRED ON THIS SYSTEM.#!\]
	POPJ	P,



;   RNFR <PATHNAME>

C.RNFR:	HLLZ	T1,CMDPTR	;GET LH OF CURRENT BYTE PTR
	HRRI	T1,RNFBUF	;BUILD POINTER TO "RENAME FROM" BUFFER
	MOVEM	T1,RNFPTR	;SAVE IT
	HRL	T1,CMDPTR	;COPY "FROM" PATHNAME TO TEMP BUFFER
	BLT	T1,RNFBUF+CMDLEN/5
;[CFE] Also copy character count.
	move	t1,CmdCnt	;[CFE] From CMD buffer
	movem	t1,RnFCnt	;[CFE]  to RNF buffer.
	EDisix	[CPOPJ##,,[SIXBIT\200 RNFR &PATHNAME STORED.#!\]]

;   RNTO <PATHNAME>

C.RNTO:	SKIPN	T1,RNFPTR	;CHECK FOR PRECEDING RNFR
	EDisix	[CPOPJ##,,[SIXBIT\504 RNFR &COMMAND MUST PRECEDE &RNTO& COMMAND.#!\]]
	move	t2,RnFCnt	;[CFE] Also load character count
	Disix	[[SIXBIT\RENAME % = %%%#!\]
		PUSHJ	P,IMPPTY	;COPY NEW PATHNAME TO PTY
		MOVEM	T1,CMDPTR
		movem	t2,CmdCnt	;[CFE] Copy count, also
		PUSHJ	P,IMPPTY]	;NOW OLD PATHNAME
	SETZM	RNFPTR		;CLEAR OLD POINTER
	MOVSI	T1,'501'	;ERROR IN FIRST LINE IS PROBABLY SYNTAX
	PUSHJ	P,GETRSP	;WAIT FOR RESPONSE
	  PJRST	CPYRSP		;ERROR, COPY RESPONSE AND QUIT
	LCHF	P1		;OK, BACKUP OVER FIRST CHAR
	MOVSI	T1,'050'	;FTP COMMENTARY
	PUSHJ	P,CPYRSP	;COPY RESPONSE TO USER
	TXNN	F,ERRFLG	;WERE THERE ANY ERRORS?
	EDisix	[CPOPJ##,,[SIXBIT\253 R&ENAME COMPLETED.#!\]]
	EDisix	[CPOPJ##,,[SIXBIT\451 R&ENAME UNSUCCESSFUL.#!\]]
;   STAT	OR	STAT <PATHNAME>

C.STAT:	PUSHJ	P,SPNOR1	;IGNORE BLANKS
	CAIE	P1,LF		;END OF LINE?
	JRST	STATDR		;NO, GO PROCESS PATHNAME
	MOVSI	T1,'050'
	EDisix	[EXP	SRVMSG
		WSIX	4,T1
		WASC	SYSNAM]
	EDisix	[[SIXBIT\100-C&URRENT PARAMETERS:#∨
    &H&OST: %   &L&ocal &S&OCKET: %   &R&emote &S&OCKET: %#!\]
		PUSHJ	P,HstPrt	;[96bit] print name
		WDEC	LclSkt
		WDEC	RmtSkt]

repeat 0,<	; these are implmeneted
	HLRZ	T1,XFRTYP
	HLRZ	T2,STRTYP
	HLRZ	T3,MODTYP
	EDisix	[[SIXBIT\    B&YTE SIZE: %   &T&YPE: %   &S&TRUCTURE: %   &M&ODE: %#!\]
		WDEC	BYTSIZ
		WCHI	(T1)
		WCHI	(T2)
		WCHI	(T3)]
> ; end of repeat 0

	TXNE	F,LGIFLG	;LOGGED IN?
	EDisix	[[SIXBIT\    S&ERVER JOB LOGGED IN UNDER [%]#!\]
		PUSHJ	P,PPNPRT]
	TXNE	F,USRFLG	;PASSWORD EXPECTED?
	EWSix	[SIXBIT\    P&ASSWORD EXPECTED#!\]
	EWSix	[Sixbit \100 E&nd of status.#!\]	;[96bit]
	POPJ	P,

;HERE TO DO STAT <PATHNAME>, I.E. DIRECTORY LISTING.
STATDR:	TXNE	F,LGIFLG	;LOGGED IN?
	JRST	.+3		;YES, PROCEED
	PUSHJ	P,FRELGI	;NO, ATTEMPT FREE LOGIN
	  POPJ	P,		;FAILED (MSG ALREADY TYPED)
	LCHF	P1		;OK, BACKUP OVER FIRST CHAR OF PATHNAME
	Disix	[[SIXBIT\DIRECT %#!\] ;OUTPUT COMMAND TO PTY
		PUSHJ	P,IMPPTY]
	MOVSI	T1,'501'	;ERROR IS PROBABLY A SYNTAX ERROR
	PUSHJ	P,GETRSP	;WAIT FOR RESPONSE
	  PJRST	CPYRSP		;ERROR--COPY RESPONSE TO USER
	MOVSI	T1,'151'	;DIRECTORY LISTING REPLY
	LCHF	P1		;BACK UP OVER FIRST CHAR
	PUSHJ	P,CPYRSP	;COPY RESPONSE TO USER
	EWSIX	[SIXBIT\200 D&IRECTORY LISTING COMPLETED.#!\]
	POPJ	P,
;   HELP
;[96bit] messages changed slightly to agree with protocol.

C.HELP:	EDisix	[Cpopj##,,HlpMsg
		  Call	HlpLst
		]

; help message.  note the percent sign at the end of the first line.
HlpMsg:	SIXBIT\200-T&HE FOLLOWING &FTP& FUNCTIONS ARE IMPLEMENTED:%#∨
    &O&NLY &ASCII& AND 36-BIT IMAGE TRANSFERS.#∨
    &STAT, LIST, NLST, DELE, RNFR, RNTO& ACCEPT WILDCARD SPECIFICATIONS.#∨
    &N&ONSTANDARD COMMANDS:#∨
      &XCWD  C&HANGE WORKING DIRECTORY.#∨
      &XSRC  C&HANGE DISK SEARCH LIST.#∨
      &XTIM  D&ISABLE INACTIVITY TIMEOUT.#∨
200 &E&nd of &HELP&.#!\

; prints out all the commands which should be printed for help.
; only called from inside EDisix, so the EFile in standard output.
HlpLst:	MOVSI	T1,-COMLEN	;CHECK EACH ONE
	SETZ	T3,		;RESET NUMBER OF ITEMS SO FAR
HELP1:	MOVE	T2,COMDSP(T1)	;GET DISPATCH WORD FOR THIS COMMAND
	TXNN	T2,CM.HLP	;WANT COMMAND LISTED?
	JRST	HELP2		;NO, SKIP IT
	SOJG	T3,.+3		;JUMP IF STILL ROOM ON THE LINE
	WSIX	[SIXBIT\#    !\] ;NO, START ANOTHER
	MOVEI	T3,↑D10		;RESET COUNTER
	WSIX	6,COMTAB(T1)	;LIST THE COMMAND
HELP2:	AOBJN	T1,HELP1	;LOOP FOR REST
Ife $FtpLog,<	;[96bit] tell if we don't allow not logged in access
	WSIX	[SIXBIT	\#    U&SER COMMAND REQUIRED TO ACCESS ANY FILES.\]
>
	Return			; now go back and print the rest.
	SUBTTL	NONSTANDARD FUNCTIONS

;    XTIM

C.XTIM:	HRROS	WATCNT		;DISABLE INACTIVITY TIMEOUT
	PJRST	COMACK		;ACKNOWLEDGE COMMAND


;    XSRC <SETSRC-STYLE SEARCH LIST>

C.XSRC:	WSix	[SIXBIT\R SETSRC#!\] ;CALL THE STANDARD DEC CUSP
	PUSHJ	P,GETRSP	;WAIT FOR RESPONSE
	  PJRST	COMNAK		;ERROR, COMPLAIN
	PUSHJ	P,PTYFLS	;FLUSH PROMPT, HELP MSG, ETC.
	Disix	[[SIXBIT\C %#!\] ;CREATE NEW SEARCH LIST AS SPECIFIED
		PUSHJ	P,IMPPTY]
	PJRST	XCMRSP		;WAIT FOR WINNING OR LOSING RESPONSE


;    XCWD <DIRECTORY>     OR    XCWD [<DIRECTORY>]

C.XCWD:	WSix	[SIXBIT\R SETSRC#!\] ;RUN SETSRC TO DO THE WORK
	PUSHJ	P,GETRSP	;WAIT FOR RESPONSE
	  PJRST	COMNAK		;CAN'T DO SETSRC STUFF
	PUSHJ	P,PTYFLS	;FLUSH RESPONSE
	FISEL	IMPCBL		;GET INPUT FROM IMP AGAIN
	CCHF	P1
	PUSHJ	P,SPNOR		;SKIP BLANKS
	CAIE	P1,"["		;DID USER TYPE SQUARE BRACKETS?
	LCHF	P1		;NO, BACKUP (SINCE IMPPTY DOES RCHF)
	;[96bit] NOTE: do NOT add a close bracket to the following
	;	 line.  it makes "XCWD [342,231]" illegal.
	Disix	[[SIXBIT\CP [%#!\] ;ENTER SETSRC COMMAND
		PUSHJ	P,IMPPTY]
XCMRSP:	PUSHJ	P,GETRSP	;WAIT FOR RESPONSE
	  PJRST	COMNAK		;LOSES, SAY WHY
	PUSHJ	P,CNCUSR	;WINS, FORCE TO COMMAND LEVEL
				;AND FALL INTO COMACK


;ROUTINE TO REPLY FOR A SUCCESSFUL MISCELLANEOUS COMMAND

C.NoOp:	;[96bit]	No-Op just acknowledges command
COMACK:	EDisix	[CPOPJ##,,[SIXBIT\200 % &COMMAND ACCEPTED.#!\]
		WNAME	CMDNAM]

;ROUTINE TO COMPLAIN ABOUT AN ERROR IN A NONSTANDARD COMMAND

COMNAK:	MOVSI	T1,'507'	;CATCHALL ERROR REPLY CODE
	PUSHJ	P,CPYRSP	;COPY RESPONSE TO USER FROM PTY
	EDisix	[CNCUSR,,[SIXBIT\507 % &COMMAND NOT ACCEPTED.#!\]
		WNAME	CMDNAM]
;    XREP	(REPLAY RECORDED PTY DIALOGUE, FOR DEBUGGING)

C.XREP:	EDisix	[Cpopj##,,[SIXBIT\050-R&EPLAY OF RECORDED &PTY& DIALOGUE:#∨
%∨
050 &E&nd of replay.#∨
200 &R&EPLAY COMPLETED.#!\]
	 Call	Replay			; do the replay
	]

Replay:	SKIPGE	T1,RECPTR	;IS ANYTHING THERE?
	  Return		; no, forget it.
	TXNN	F,WRPFLG	;YES, DOES IT WRAP AROUND?
	MOVE	T1,RECPT0	;NO, START AT BEGINNING OF BUFFER
XREP1:	CAMN	T1,RECPTZ	;AT END?
	MOVE	T1,RECPT0	;YES, GO BACK TO BEGINNING
	ILDB	T2,T1		;GET A CHAR
	WCHI	(T2)		;SEND IT TO IMP
	CAME	T1,RECPTR	;BACK WHERE WE STARTED?
	JRST	XREP1		;NO, CONTINUE
	CAIE	T2,LF		;YES, WERE WE AT EOL?
	W2CHI	CRLF		;NO, START FRESH LINE
	Return			; all done: go back and print the ending
	SUBTTL	SUBROUTINES

;ROUTINE TO OPEN THE SUBJOB'S IMP DATA CONNECTION.
;	MOVE	T1,[SIXBIT IMP LOGICAL DEVICE NAME TO BE USED]
;	MOVE	T2,[TYPE INDEX -- 0=ASCII, 1=IMAGE]
;	PUSHJ	P,DoOpen
;	  ERROR RETURN--MESSAGE ALREADY TYPED
;	OK RETURN

DoOpen:
	EDisix	[[SIXBIT\255 SOCK %#!\] ;STANDARD MESSAGE
		WDEC	LCLSkt]
	Disix	[[SIXBIT\IMP CONNECT %: % /LOCAL:%/Absolute/REMOTE:%#!\]
		WNAME	T1
		Pushj	P,HstNoo	;[96bit] print host number
		WDEC	LCLSKT
		WDEC	RmtSkt
		]
	MOVSI	T1,'454'	;MESSAGE CODE IN CASE ERROR
	PUSHJ	P,GETRSP	;EAIT FOR RESPONSE
	  PJRST	CPYRSP		;ERROR, COPY MESSAGE TO USER AND QUIT
	PUSHJ	P,PTYFLS	;OK, FLUSH OUTPUT
	JRST	CPOPJ1##		;TAKE GOOD RETURN
;ROUTINE TO WAIT FOR COMPLETION OF A DATA TRANSFER FUNCTION
;	PUSHJ	P,XFRCHK
;	  ERROR--MESSAGE ALREADY PRINTED AND CONNECTION CLOSED
;	OK--NOTHING PRINTED, CONNECTION NOT CLOSED, OUTPUT NOT FLUSHED

XFRCHK:	MOVEI	T1,1		;WAIT ONE SECOND FOR THINGS TO GET STARTED
	SLEEP	T1,
	PUSHJ	P,PTYCHK	;HAS ANYTHING COME BACK FROM THE SUBJOB?
	  EDisix [XFRCK1,,[SIXBIT\250 % &STARTED.#!\]
		WNAME	CMDNAM]
	PUSHJ	P,GETRSP	;YES, SEE WHAT IT WAS
	  JRST	XFRERR		;AN ERROR, GO COMPLAIN
	EDisix	[CPOPJ1##,,[SIXBIT\250 % &STARTED.#!\]
		WNAME	CMDNAM]

;HERE IF NO RESPONSE IN THE FIRST SECOND
XFRCK1:	PUSHJ	P,GETRSP	;WAIT FOR RESPONSE
	  JRST	XFRERR		;ERROR, GO COMPLAIN
	JRST	CPOPJ1##		;OK, SKIP RETURN

;HERE ON ERROR RESPONSE DURING DATA TRANSFER
XFRERR:
	MOVSI	T3,'507'	;if code is 507 don't change to 454
	CAME	T3,T1
	MOVSI	T1,'454'	;CATCHALL ERROR MESSAGE
	PUSHJ	P,CPYRSP	;COPY ERROR MESSAGE TO USER
	PUSHJ	P,CNCUSR	;FORCE TO COMMAND LEVEL
	WSix	[SIXBIT\IMP CLOSE/SELF#!\] ;CLOSE OPEN CONNECTION(S)
	PJRST	PTYFLS		;FLUSH ANYTHING THAT COMES BACK UP
;ROUTINE TO PERFORM A "FREE" FTP LOGIN
;	PUSHJ	P,FRELGI
;	  ERROR--MESSAGE ALREADY PRINTED
;	OK--LGIFLG HAS BEEN SET

FRELGI:
Ife $MLogin ! $FtpLog,<	;[96bit] if no free logins, complain and return
	EDisix [Cpopj,,[SIXBIT\504 USER &COMMAND REQUIRED BEFORE %.#!\]
		WNAME	CMDNAM]
>	;end ife ftfree
Ifn $MLogin ! $FtpLog,<	;[96bit] want free logins of some kind?
	TXZE	F,USRFLG	;LEFTOVER USER NAME?
	PUSHJ	P,CNCUSR	;YES, FLUSH IT
Ifn $MLogin,<	;[96bit] any special mail stuff?
	TXNN	P4,CM.LGM	;WANT FREE LOGIN FOR MLFL
	JRST	FRELG1		;NO
Ifn MailPPn,<	;[96bit] need to chgppn?
	MovX	T1,MailPPn		;[96bit] change the current ppn
	CHGPPN	T1,
	   JFCL
>	;end ifn MailPPn
	HRRZI	T1,MailLogin	;[96bit] set up the proper ppn
	TXO	F,TLogin	;[96bit] remember to log this out
	JRST	FRELG2
FRELG1:
>	;end ifn $Mlogin
Ife $FtpLog,<	;[96bit] if not allowing normal FTPs without USER,
		;	 then complain and return
	EDisix [Cpopj,,[SIXBIT\504 USER &COMMAND REQUIRED BEFORE %.#!\]
		WNAME	CMDNAM]
>
Ifn $FtpLog,<	;[96bit] logging in for ftp?
Ifn FtpPPn,<	;[96bit] want a chgppn for ftp?
	MovX	T1,FtpPPn	;[96bit] get the PPn to change to
	CHGPPN	T1,		;YES, DO IT
	  JFCL			;DON'T CARE IF FAILS
>
	HRRZI	T1,FtpLogin	;[96bit] get name of free account
>	;end ifn $ftplog
FRELG2:	Disix	[[SIXBIT\LOGIN %#!\] ;ATTEMPT TO LOGIN
		WSIX	(T1)]
	PUSHJ	P,CHKLGI	;SEE HOW IT DID
	  PJRST	[		; totally invalid
		 TXZ F,TLogin	;[96bit] not logged in
		 PJRST LGIERF
		]
	  EDisix [CNCUSR,,[SIXBIT\504 USER &COMMAND REQUIRED BEFORE %.#!\]
		  WNAME	CMDNAM]
	PUSHJ	P,SJBPPN	;WE DID, GET SUBJOB PPN
	MOVEM	T1,PRJPRG	;STORE IT
	TXO	F,LGIFLG	;REMEMBER LOGIN SUCCESS
	LCHF	P1		;RETAIN FIRST CHAR OF RESPONSE
	MOVSI	T1,'050'	;CODE FOR GENERAL FTP INFO
	PUSHJ	P,CPYRSP	;COPY LOGIN MESSAGES TO USER
	FISEL	IMPCBL		;POINT TO INPUT FILE BLOCK AGAIN
	JRST	CPOPJ1##		;TAKE SUCCESS RETURN
>	;end Ifn $MLogin ! $FtpLog


;ROUTINES TO HANDLE LOGIN FAILURE AND PRINT MESSAGE
;	PUSHJ	P,LGIERR OR LGIERF
;	ALWAYS RETURN HERE, MESSAGE PRINTED, PTY OUTPUT FLUSHED
;  LGIERR USES CODE 431, LGIERF USES 504.
;[CFE]	LGIERF now uses 436 since it's just a temporary error condition!

LGIERR:	MOVSI	T1,'431'	;ERROR CODE FOR NORMAL LOGIN ATTEMPT
	TXZA	F,USRFLG	;CLEAR USER-NAME-GIVEN FLAG
;[CFE] LGIERF:	MOVSI	T1,'504'	;ERROR CODE FOR FREE LOGIN ATTEMPT
LGIERF:	MOVSI	T1,'436'	;[CFE] ERROR CODE FOR FREE LOGIN ATTEMPT
	RCHF	P1		;GET FIRST CHAR AFTER QUESTION MARK
	CAIE	P1,"("		;ERROR NUMBER IN PARENTHESES?
	JRST	.+4		;NO
	RCHF	P1		;YES, FLUSH LEFT PAREN
	RCHF	P1		;FLUSH ERROR CODE
	JRST	.+2		;CAUSE RIGHT PAREN TO BE FLUSHED
	LCHF	P1		;BACKUP IF DIDN'T SEE "("
	PUSHJ	P,CPYRSP	;COPY RESPONSE TO USER
	PJRST	CNCUSR		;FORCE SUBJOB TO COMMAND LEVEL.
;ROUTINE TO LOG THE SUBJOB OUT.
;	PUSHJ	P,LGOUSR
;	RETURN HERE AFTER SUBJOB LOGGED OUT

LGOUSR:	PUSHJ	P,CNCUSR	;FORCE TO MONITOR LEVEL
	MOVSI	T1,'050'	;TREAT REPLIES AS COMMENTARY
IfDef KjFunc,<	;[96bit] is there a brain damaged logout?
	KjFunc			;[96bit] yes: use it.
>
IfNDef KjFunc,<	;[96bit] no: use k/b
	WSix	[SIXBIT\KJOB /B#!\] ;PRESERVE ANY FILES POSSIBLE
	PJRST	CPYRSP		;COPY RESPONSE TO USER IF HE'S STILL THERE
>

Repeat 0,<	;[96bit] do this with macros now
	HRRZ	T2,BYEDSP(H)	;GET DISPATCH FOR DESIRED LOGOUT PROTOCOL
	JRST	(T2)		;  FOR THIS HOST

KJOB.F:	WSix	[SIXBIT\KJOB /F#!\];CMU- SAVE ALL FILES
	PUSHJ	P,CPYRSP	;COPY THIS
	TXNN	F,ERRFLG	;ERROR (OVER QUOTA)
	POPJ	P,		;NOPE ALL IS GOODNESS
	PUSHJ	P,CNCUSR	;STOP HIM
	WSix	[SIXBIT/CORE 0#!/];FREE ALL HIS CORE
	PJRST	PTYFLS		;AND GO AWAY

KJOB.B:	WSix	[SIXBIT\KJOB /W/B#!\] ;PRESERVE ANY FILES POSSIBLE
	PJRST	CPYRSP		;COPY RESPONSE TO USER IF HE'S STILL THERE
>	;end of repeat 0


;ROUTINE TO SEND CONTROL-C'S TO THE SUBJOB AND FLUSH ALL RESULTING
;   OUTPUT.
;	PUSHJ	P,CNCUSR
;	ALWAYS RETURN HERE

CNCUSR:	FOSEL	PTYOBL		;SELECT INPUT AND OUTPUT PTY
	FISEL	PTYIBL
	W2CHI	3B28+3		;SEND 2 ↑C'S
	WCHI	LF		;MAKE BUFFER BE FORCED OUT
				;FALL INTO PTYFLS


;ROUTINE TO FLUSH ALL PTY OUTPUT UNTIL IT GOES INTO INPUT WAIT.
;	PUSHJ	P,PTYFLS
;	ALWAYS RETURN HERE

PTYFLS:	FISEL	PTYIBL		;SELECT PTY FOR INPUT
PtyFl1:	RCHF	P1		;GET A CHAR
	JUMPN	P1,PtyFl1	;TRY AGAIN IF GOT ANYTHING
	POPJ	P,		;RETURN WHEN NOTHING MORE


;ROUTINE TO FLUSH PTY OUTPUT UNTIL EITHER A LINE FEED IS ENCOUNTERED
;   OR THE SUBJOB GOES INTO TTY INPUT WAIT.
;	PUSHJ	P,PTYF1L
;	ALWAYS RETURN HERE

PTYF1L:	FISEL	PTYIBL		;SELECT PTY FOR INPUT
PtyF11:	RCHF	P1		;GET A CHAR
	CAIE	P1,LF		;LINE FEED?
	JUMPN	P1,PtyF11	;NO, FLUSH IF NOT END OF OUTPUT
	POPJ	P,
;ROUTINE TO WAIT FOR A RESPONSE FROM THE SUBJOB.
;	PUSHJ	P,GETRSP
;	  ERROR--RESPONSE LINE BEGAN WITH "?"
;	OK RETURN, FIRST CHAR OF RESPONSE IN P1
;   GETRSP FLUSHES BLANK LINES WHILE SEARCHING FOR ITS RESPONSE.

GETRSP:	FISEL	PTYIBL		;SELECT PTY INPUT
GETRS1:	RCHF	P1		;GET A CHAR
	JUMPE	P1,CPOPJ1##	;SKIP RETURN IF GOT NONE
	CAIE	P1,CR		;CARRIAGE RETURN?
	CAIN	P1,LF		;LINE FEED?
	JRST	GETRS1		;YES, FLUSH
	CAIE	P1,"?"		;ERROR RESPONSE?
	JRST	CPOPJ1##		;NO, SKIP RETURN
	;[96bit] check for "?%", which we interpret as
	;	 "user not found" type errors: completely fatal.
	TXNN	F,MAILFG!MLFLFG	;inside mail or mlfl?
	POPJ	P,		;no normal error return
	RCHF	P1		;yes, get next char
	CAIN	P1,"%"		;unknown user type error?
	MOVSI	T1,'507'	;yes, special error code
;[CFE, 16 Apr 81] Make sure a legitimate first character gets through.
	CAIE	P1,"%"		;[CFE] Unless a "%",
	  LCHF	P1		;[CFE]  save it for diagnostic msg.
	POPJ	P,


;ROUTINE TO CHECK WHETHER THE SUBJOB HAS BEEN SUCCESSFULLY LOGGED IN
;AFTER THE LOGIN COMMAND WAS SENT TO IT.
;	Disix	[[SIXBIT\LOGIN %#!\]
;		 PUSHJ	P,WHATEVER]
;	PUSHJ	P,CHKLGI
;	  SOMETHING VERY WRONG, LOGIN GAVE ERROR
;	  NEEDS PASSWORD STILL.
;	SUBJOB LOGGED IN; JOBSTS BITS IN T1

CHKLGI:	PUSHJ	P,GETRSP	;GET RESPONSE FROM LOGIN
	  POPJ	P,		;NOT GOOD, LET CALLER HANDLE
	PUSHJ	P,PTYF1L	;IGNORE THIS LINE (JOB #, TTY#, ETC.)
	jumpn	p1,chklgi	; if there are more chars in the buffer,
				; continue to check for errors.

	; now check to see where we stand
CHKLG1:	MOVEI	T1,PTY		;TAKE A LOOK AT PTY STATUS
	JOBSTS	T1,		;TO CHECK LOGGED IN BIT.
	  PUSHJ	P,Idiocy	;DAMN IT, I JUST HAD ONE!
	txne	t1,jb.uoa	; more output available?
	  jrst	ChkLgi		; yes: go back to error checking
;[CFE]	txne	t1,jb.uli	; well, is it logged in?
;[CFE]	  pjrst	cpopj2		; yes: give an excellent return
;[CFE] Wait for logged-in *and* input wait.
	txnn	t1,Jb.ULI	;[CFE] Logged in?
	  jrst	ChkLg2		;[CFE]  No; skip ahead.
	txnn	t1,Jb.UDI	;[CFE] Awaiting input?
	  jrst	ChkLg3		;[CFE]  no; wait for this bit.
CPopj2:	aos	(p)		; Double-skip (excellent) return.
	jrst	CPopj1##
ChkLg2:
;[CFE]	txne	t1,jb.udi	; input wait?
;[CFE]	  pjrst	cpopj1##	; yes: must want a password
;[CFE]	txne	t1,jb.uml	; at monitor level (and NOT logged in!)
;[CFE]	  pushj	p,idiocy	; this situation should be looked at
;[CFE] No, JB.UDI can happen in monitor mode, also.
	txnn	t1,Jb.UDI	;[CFE] Awaiting input?
	  jrst	ChkLg3		;[CFE]  No, wait for another event.
	txne	t1,Jb.UML	;[CFE] Are we in monitor mode?
	  popj	p,		;[CFE] Yes; something went badly wrong.
	jrst	CPopj1##	;[CFE] No; we must await a password.
ChkLg3:	MOVEI	T1,1		;NONE. WAIT AWHILE
	SLEEP	T1,		; FOR LOGIN TO DO ITS THING
	pushj	p,ImpChk	;[CFE] Check this while we wait
	JRST	CHKLG1		;AND LOOK AGAIN


;ROUTINE TO RETURN THE SUBJOB'S PPN
;	PUSHJ 	P,SJBPPN
;	ALWAYS RETURN HERE WITH PPN IN T1

SJBPPN:	MOVEI	T1,PTY		;PTY CHANNEL
	JOBSTS	T1,		;GET CONTROLLED JOB NUMBER
	  PUSHJ	P,Idiocy
	MOVSI	T1,(T1)		;GET PPN FOR THAT JOB
	HRRI	T1,.GTPPN
	GETTAB	T1,
	  PUSHJ	P,Idiocy
	POPJ	P,
;ROUTINE TO COPY A RESPONSE FROM THE PTY TO THE IMP.
;	MOVE	T1,[4-CHARACTER SIXBIT RESPONSE CODE]
;	PUSHJ	P,CPYRSP
;	ALWAYS RETURN HERE

CPYRSP:	FISEL	PTYIBL		;SELECT PTY INPUT
	FOSEL	IMPOBL		;IMP OUTPUT
	TXZ	F,ERRFLG	;CLEAR ERROR FLAG
CPYRS1:	RCHF	P1		;GET A CHAR
	JUMPE	P1,CpyRs4	;RETURN IF NO MORE
	CAIE	P1,CR		;BLANK LINE?
	CAIN	P1,LF
	JRST	CPYRS1		;YES, FLUSH
;[CFE] Flush double-"." after a MAIL command; ignore leading "."s.
	cain	p1,"."		;[CFE] Is it a monitor dot?
	  jrst	CpyRs1		;[CFE]  yes; ignore it.
	CAIN	P1,"?"		;AN ERROR?
	TXO	F,ERRFLG	;YES, REMEMBER IT
	MOVEI	T2,(P1)		;SAVE THE FIRST CHAR
CpyRsX:	RCHF	P1		;GET NEXT CHAR
	JUMPE	P1,CpyRs4	;QUIT IF NONE (CHAR WAS A PROMPT)
	CAIN	P1,4		;CONTROL-D?
	JRST	CpyRsX		;YES (LOGIN HACK ON SOME ERRORS)
	WSIX	4,T1		;OUTPUT MESSAGE CODE
	WCH	T2		;OUTPUT FIRST CHARACTER
	SKIPA			;KEEP RESPNSE CODE FOR ALL LINES
CPYRS2:	RCHF	P1		;GET A CHAR
	JUMPE	P1,CPYRS3	;JUMP IF ENDED IN MIDDLE OF LINE
	WCH	P1		;OUTPUT CHAR TO IMP
	CAIE	P1,LF		;END OF LINE?
	JRST	CPYRS2		;NO, KEEP COPYING
	JRST	CPYRS1		;YES, START NEW LINE

;HERE IF ENDED IN MIDDLE OF LINE (SHOULDNT)
CPYRS3:	W2CHI	CRLF		;CAUSE LINE TO GO OUT TO IMP ANYWAY
CpyRs4:	FoSel	PtyObl		; return to pty output.
	POPJ	P,


;ROUTINE TO COPY A LINE OF TEXT FROM THE IMP TO THE PTY.
;   THE CRLF AT THE END IS NOT INCLUDED
;	PUSHJ	P,IMPPTY
;	ALWAYS RETURN HERE

IMPPTY:	FISEL	IMPCBL		;SELECT COMMAND BUFFER INPUT
	FOSEL	PTYOBL		;SELECT PTY OUTPUT
IMPPT1:	RCHF	P1		;GET A CHAR
	CAIE	P1,CR		;RETURN OR LINEFEED?
	CAIN	P1,LF
	POPJ	P,		;YES, DONE
	WCH	P1		;NO, SEND TO PTY
	JRST	IMPPT1		;BACK FOR MORE
;ROUTINE TO INPUT A DECIMAL NUMBER FROM THE CURRENT INPUT DEVICE
;   AND RETURN IT IN T1.
;	PUSHJ	P,GETDEC
;	  ERROR--FIRST CHAR NOT A DIGIT
;	OK--NUMBER IN T1

GETDEC:	PUSHJ	P,SPNOR1	;GET FIRST CHAR AND IGNORE SPACES
	TXNN	P2,DIGIT	;IS FIRST CHAR A DIGIT?
	POPJ	P,		;NO--ERROR
	SETZ	T1,		;YES, INITIALIZE NUMBER
GETDE1:	IMULI	T1,↑D10		;ACCUMULATE DIGIT
	ADDI	T1,-"0"(P1)
	RCHF	P1		;GET NEXT
	TRNE	P2,DIGIT	;ALSO A DIGIT?
	JRST	GETDE1		;YES, USE IT
	PUSHJ	P,SPNOR		;NO, IGNORE TRAILING BLANKS
	JRST	CPOPJ1##		;SKIP RETURN


;ROUTINE TO IGNORE BLANKS
;	PUSHJ	P,SPNOR		;USES CURRENT P1
;	PUSHJ	P,SPNOR1	;FETCHES  NEW CHAR BEFORE TESTING

SPNOR1:	RCHF	P1		;FETCH A CHARACTER
SPNOR:	CAIE	P1," "		;BLANK?
	CAIN	P1,CR		;CARRIAGE RETURN (WHICH WE IGNORE)
	JRST	SPNOR1		;YES, FLUSH IT
	POPJ	P,		;NO, RETURN


;ROUTINE TO CHECK FOR PTY OUTPUT
;	PUSHJ	P,PTYCHK
;	  NO OUTPUT AVAILABLE
;	OUTPUT IS AVAILABLE
;   T1 CONTAINS JOBSTS BITS ON EITHER RETURN AND IS THE ONLY AC CLOBBERED

PTYCHK:	MOVE	T1,PTSPNT	;ALSO, SEE IF ANYTHING BUFFERED (NORMALLY WON'T BE)
	CAME	T1,PTRPNT	;MEANING RETRIEVE AND STORE POINTERS ARE DIFFERENT
	JRST	CPOPJ1##		;YES, SKIP RETURN

;ROUTINE TO SEE IF PTY BUFFERS HAVE DATA TO READ IN
PTBCHK:	MOVEI	T1,PTY		;SET PTY CHANNEL
	JOBSTS	T1,		;CHECK STATE OF SUBJOB
	  PUSHJ	P,Idiocy		;HMMM...
	TXNE	T1,JB.UOA	;SUBJOB OUTPUT AVAILABLE?
	AOS	(P)		;THEY ARE...DATA
	POPJ	P,		;NOPE, PTY QUIET
;ROUTINE TO BUFFER PTY OUTPUT SO WE CAN SEND IT SOME DATA

PTYSAV:	PUSH	P,U2		;SAVE CURRENT IO BLOCK
	MOVEI	U2,PTYIBL	;AND POINT TO PTY
PTYS1:	PUSHJ	P,PTYBUF	;GET A CHARACTER FROM PTY
	JUMPE	U1,PTYS2	;0 SAYS END
	SOSLE	PTSCNT		;ROOM TO SAVE THIS ONE?
	IDPB	U1,PTSPNT	;YEP, HE LUCKS OUT
	JRST	PTYS1		;AND TRY FOR ANOTHER, OVERFLOW WILL BE LOST

PTYS2:	POP	P,U2		;RESTORE
	POPJ	P,		;AND RETURN


;ROUTINE TO DO THE RCH OPERATION FOR THE PTY.

PTYRCH:	MOVE	U3,PTRPNT	;PICKUP PTY RETRIEVAL POINTER
	CAMN	U3,PTSPNT	;IS IT THE SAME AS THE STUFF POINTER?
	JRST	PTYBUF		;YES, THEREFORE NO DATA SAVED TO READ, GET FROM BUFFER
	ILDB	U1,U3		;GET NEXT CHAR TO PROCESS
	CAME	U3,PTSPNT	;NOW ARE WE EQUAL?
	JRST	[MOVEM	U3,PTRPNT;NO, SAVE POINTER FOR NEXT TIME
		 POPJ	P,]
	MOVE	U3,[PTYRSH,,PTYRSL];SAME, REINITIALIZE AREA
	BLT	U3,PTYRSE-1	;FOR THE NEXT DATA WE HAVE TO BUFFER
	POPJ	P,		;MEANWHILE, LET THE LAST SAVED CHAR BE PROCESSED

PTYBUF:;ROUTINE TO READ NEXT CHARACTER FROM PTY BUFFERS

	SKIPLE	FILCTR(U2)	;IS THERE ANY BUFFERED DATA?
	JRST	PTYRC1		;YES, GET IT NOW
	MOVE	U1,T1		;NO, SAVE T1
	PUSHJ	P,PTBCHK	;SEE IF PTY HAS ANY MORE OUTPUT DATA
	  JRST	PTYRC2		;IT DOESN'T
	MOVE	T1,U1		;IT DOES.  RESTORE T1 AND PROCESS IT

;HERE WHEN DATA IS AVAILABLE
PTYRC1:	PUSHJ	P,I1BYTE##	;CALL STANDARD BYTE ROUTINE
	JUMPE	U1,PTYBUF	;FLUSH NULLS
	PJRST	RECPUT		;PRINT AND/OR RECORD THE CHAR

;HERE WHEN NO DATA IS AVAILABLE
PTYRC2:	EXCH	U1,T1		;RESTORE T1, PUT JOBSTS BITS IN U1
	TXNE	U1,JB.UDI	;SUBJOB WAITING FOR INPUT?
	TDZA	U1,U1		;YES
	MOVEI	U1,1		;NO, SET SLEEP TIME
	JUMPE	U1,CPOPJ##	;RETURN WITH NULL IF NO MORE OUTPUT
	SLEEP	U1,		;SLEEP ONE SECOND
	PUSHJ	P,IMPCHK	;MAKE SURE TELNET CONNECTION STILL OPEN
	JRST	PTYBUF		;TRY AGAIN
;ROUTINE TO DO WCH OPERATION FOR IMP AND PTY, WHICH WANT TO BREAK
;   ON END-OF-LINE.

IMPWCH:	TXNN	F,OPNFLG	;TELNET CONNECTION OPEN?
	POPJ	P,		;NO, FLUSH IMP OUTPUT
PTYWCH:	PUSHJ	P,O1BYTE##	;CALL STANDARD BYTE OUTPUT ROUTINE
	CAIN	U2,PTYOBL	;PTY OUTPUT?
	PUSHJ	P,RECPUT	;YES, PRINT AND/OR RECORD THE CHAR
	MOVEI	U3,(U1)	;COPY CHARACTER JUST OUTPUT
	ANDI	U3,177		;7 BITS ONLY
	CAIE	U3,LF		;REACHED END OF LINE?
	POPJ	P,		;NO
	CAIE	U2,PTYOBL	;GOING OUT TO PTY?
	JRST	PTYW2		;NO, CAN DO OUTPUT
PTYW1:	MOVEI	U3,PTY		;LET'S SEE IF PTY WANTS DATA
	JOBSTS	U3,
	  JRST	PTYW2		;FAILED? SHOULDN'T HAVE
	TXNE	U3,JB.UOA	;ANY OUTPUT FROM PTY THAT WE MUST STORE FIRST?
	JRST	[PUSHJ P,PTYSAV	;YES, GO BUFFER EVERYTHING IN SIGHT
		 JRST	PTYW1]	;AND SEE IF WE CAN OUTPUT NOW
	TXNE	U3,JB.UDI	;OKAY TO OUTPUT TO?
	JRST	PTYW2		;YES, DO SO
	MOVX	U3,HB.RWJ!HB.RPT!↑D1000;WAIT FOR PTY ACTIVITY
	HIBER	U3,		;DO SO
	  JRST	[MOVEI	U3,1	;FAILED (10/40) SLEEP A SECOND
		 SLEEP	U3,
		 pushj	p,ImpChk ;[CFE] Check IMP connection
		 JRST	PTYW1]	;AND TRY AGAIN
	pushj	p,ImpChk	;[CFE] Ensure connection still there
	JRST	PTYW1		;TRY AGAIN FROM HIBERNATE

PTYW2:	PUSHJ	P,UXCT2##	;YES, CAUSE OUTPUT TO BE SENT
	  OUT
	  POPJ	P,		;OK
	MOVE	U1,FILER2(U2)	;ERROR, TAKE ERROR DISPATCH
	PJRST	UERXIT##


;ROUTINE TO MONITOR AND/OR RECORD CHARACTER IN U1 FOR LATER PLAYBACK.
;	MOVE	U1,[ASCII CHARACTER]
;	PUSHJ	P,RECPUT
;	ALWAYS RETURN HERE, ALL AC'S PRESERVED

RECPUT:	TXNE	F,SLGFLG	;MONITORING?
	OUTCHR	U1		;YES, PRINT THE CHARACTER
	EXCH	U2,RECPTR	;GET CURRENT RECORDING POINTER
	CAME	U2,RECPTZ	;AT END OF BUFFER?
	JRST	.+3		;NO
	TXO	F,WRPFLG	;YES, REMEMBER WE WRAPPED AROUND
	MOVE	U2,RECPT0	;RESET POINTER TO START
	IDPB	U1,U2		;STORE CHAR IN BUFFER
	EXCH	U2,RECPTR	;RESTORE U2 AND STORE NEW POINTER
	POPJ	P,		;RETURN

RECPTZ:	POINT	7,RECBUF+RECSIZ-1,34	;POINTER TO LAST CHAR OF BUFFER
;ROUTINE TO DO THE RCH OPERATION FROM THE IN-CORE IMP BUFFER.

RCHICB:
;[CFE] Provide overflow-safe character processing: obey a count of
;[CFE]  the number of characters saved in the buffer.  Return LFs
;[CFE]  when we're at end of buffer.
	sosge	CmdCnt		;[CFE] Decr and test count
	  jrst	[movei	u1,12	;[CFE] Out of chars!  Return a LF.
		 popj	p,]	;[CFE]
	ILDB	U1,CMDPTR	;GET A CHAR
	CAIL	U1,"A"+40	;LOWER CASE?
	CAILE	U1,"Z"+40
	POPJ	P,		;NO
	TXNN	F,MAILFG	;AND NOT MAIL?
	SUBI	U1,40		;YES, MAKE UPPER
	POPJ	P,


Repeat 0,<	; remove these, and their UUOs (SixImp, SixPty,
		; DSxPty, DSxImp), and replace them with error
		; channel for imp output, normal output for pty output

;VARIOUS SPECIAL UUO HANDLERS

UDSXPT::MOVEI	U2,PTYOBL	;DISIX OPERATION TO PTY
	JRST	.+2

UDSXIM::MOVEI	U2,IMPOBL	;DISIX OPERATION TO IMP
	MOVEM	U2,OFILE##	;STORE CORRECT POINTER TO FILE BLOCK
	PJRST	UDISIX##

USIXPT::MOVEI	U2,PTYOBL	;WSIX OPERATION TO PTY
	JRST	.+2

USIXIM::MOVEI	U2,IMPOBL	;WSIX OPERATION TO IMP
	MOVEM	U2,OFILE##	;STORE CORRECT FILE BLOCK POINTER
	SETZ	U3,		;ONLY INDEFINITE WSIX ALLOWED!
	PJRST	UWSIX##		;DO OPERATION

>;	end of Repeat 0


;ROUTINE TO HANDLE IMPOSSIBLE ERRORS

Idiocy:	SOS	T1,(P)		;GET ERROR ADDRESS
	EDisix	[C.BYE,,[SIXBIT\435 A&N IMPOSSIBLE ERROR HAS OCCURRED AT LOCATION %#!\]
		WOCTI	(T1)]
;ROUTINE TO MAKE SURE THE TELNET CONNECTION IS STILL OPEN.
;	PUSHJ	P,IMPCHK
;	RETURN HERE IF STILL OPEN
;   INITIATES "BYE" COMMAND IF CONNECTION HAS CLOSED
;   NO AC'S CLOBBERED

IMPCHK:	TXNN	F,OPNFLG	;DO WE THINK IT'S OPEN NOW?
	POPJ	P,		;NO, JUST FLUSHING JOB OR SOMETHING
	PUSHJ	P,SAVE1##	;SAVE P1
	MOVEI	P1,CONBLK	;DO STATUS OPERATION
	IMPUUO	P1,
	  JRST	ImpEro		;CONNECTION MUST HAVE GONE AWAY
	LDB	P1,[POINT 6,.IBSTT+CONBLK,35] ;GET STATE
	CAIN	P1,.ISEst	;OPEN?
	  POPJ	P,		;YES, RETURN
;HERE ON IMP ERROR (PROBABLY CONNECTION CLOSED)
ImpEro:	TXZ	F,OPNFLG	;CLEAR IMP OPEN FLAG
	JRST	C.BYE		;FORCE A BYE COMMAND


;HERE ON ERROR FROM THE PTY. TELL USER WHAT HAPPENED, THEN CLOSE
PTYERR:	pushj	p,ImpChk	;[CFE] Check IMP before write, also.
	EDisix	[C.BYE,,[SIXBIT\435 %#!\]
		 ERROUT	PTYOBL]	;REPORT PTY ERROR AND BREAK CONNECTION


;ROUTINE TO PRINT C(PRJPRG) AS REGULAR PPN OR CMUPPN
;	PUSHJ	P,PPNPRT
;	ALWAYS RETURN HERE

PPNPRT:	WPPN	PRJPRG		;PRINT PPN THE REGULAR WAY
	POPJ	P,


;ROUTINE TO PRINT THE NAME OR NUMBER OF THE FOREIGN HOST
; uses currently selected output, which will be the IMP if called
; from "inside" a EDisix.
;	PUSHJ	P,HSTPRT
;	ALWAYS RETURN HERE

HSTPRT:
;[96bit]SKIPE	SXBHST		;DO WE KNOW WHO HE IS?
;[96bit]DISIX	[CPOPJ##,,[SIXBIT\%-%!\]
;[96bit]	WNAME	SXBHST
;[96bit]	WNAME	SXBHST+1]
;[96bit]WDEC	HSTADR		;NO, JUST PRINT IN DECIMAL
	Skipg	HsName		;[96bit] know the name?
	  Jrst	HstNoo		;[96bit] no: print the number
	WASC	@HsName		;[96bit] print the name
	Popj	p,		;[96bit] and return

HstNoo:	;[96bit] subroutine to print host number in new format
	Save	T1		;[96bit] save T1
	LDB	T1,HstPnt	;[96bit] get host number
	WDEC	T1		;[96bit] and print it
	WCHI	"."		;[96bit] separating dot
	LDB	T1,SitPnt	;[96bit] get site number
	WDEC	T1		;[96bit] and print it
	WCHI	"."		;[96bit] separating dot
	LDB	T1,NetPnt	;[96bit] and net number
	WDEC	T1		;[96bit] and print it
	Jrst	Tpopj		;[96bit] restore T1 and return

HstPnt:	Pointr	(HstAdr,Ih.Hst)		;[96bit] pointer to host number
SitPnt:	Pointr	(HstAdr,Ih.Imp)		;[96bit] pointer to net number
NetPnt:	Pointr	(HstAdr,Ih.Net)		;[96bit] to network number

;[96bit] subroutine to set a new host address.  checks HstTmp:
;	 if non-zero, moves value into HstAdr, and looks up the
;	 name and puts it in HsName.  if can't find name, HsName
;	 gets -1.
;NOTE: this routine CANNOT be called from withing a LUUO, like
;      in the instruction list for a EDisix, for example.
SetNam:	Push	P,T1		;[96bit] save a reg
	Skipn	T1,HstTmp	;[96bit] new address?
	  Jrst	Tpopj		;[96bit] no: just return
	Movem	T1,HstAdr	;[96bit] save new address
	Clearm	HstTmp		;[96bit] if it's new, forget newness.
	Setom	HsName		;[96bit] assume we're going to fail
	Push	P,T2		;[96bit] save reg from nasty HstNum
	PUSHJ	P,HstNum##	;FIND OUT WHAT IT'S NAME IS
	  Jfcl			; couldn't get tables.
	  Jrst	T2Popj		; couldn't find entry.  flag is set
	hrrzm	T1,HsName	; remember
T2Popj:	Pop	P,T2		;[96bit] restore T2
TPopj:	Pop	P,T1		;[96bit] restore T1
	Popj	P,		;[96bit] return
	SUBTTL	INITIAL FILE BLOCKS

	XALL

;ICP OUTPUT
ICPBLH:	FILE	IMP,O,ICPBLK,<DEV(ICP),STAT(6)>

;IMP INPUT OVER TELNET CONNECTION
IMPIBH:	FILE	IMP,I,IMPIBL,<DEV(TTY),STAT(.IOASC),OPEN(BYEFR2)
		,INPUT(ImpEro),EOF(ImpEro),OTHER(IMPOBL)>

;IMP OUTPUT OVER TELNET CONNECTION
IMPOBH:	FILE	IMP,O,IMPOBL,<DEV(FTPSRV),STAT(.IOASC),OPEN(BYEFR3)
		,OUTPUT(ImpEro),OTHER(IMPIBL),<INST(<PUSHJ P,IMPWCH>)>>
;PTY INPUT (SUBJOB'S OUTPUT)
PTYIBH:	FILE	PTY,I,PTYIBL,<DEV(PTY),STAT(.IOASC),OPEN(PTYTRY)
		,INPUT(PTYERR),EOF(PTYERR),OTHER(PTYOBL)
		,<INST(<PUSHJ P,PTYRCH>)>>

;PTY OUTPUT (SUBJOB'S INPUT)
PTYOBH:	FILE	PTY,O,PTYOBL,<DEV(PTY),STAT(.IOASC),OPEN(PTYTRY)
		,OUTPUT(PTYERR),OTHER(PTYIBL),<INST(<PUSHJ P,PTYWCH>)>>

;INPUT FROM IMP COMMAND BUFFER
IMPCBH:	PFILE	IMPCBL,<PUSHJ P,RCHICB>
	SUBTTL	LOW-SEGMENT INITIALIZATION DATA

FILLH:

; CONBLK (TELNET CONNECTION BLOCK)
	SIXBIT	\FTPSRV\
	0
	EXP	TLNSKT
	0			;[96bit]
	0

;DEFAULT FTP TRANSFER PARAMETERS
	EXP	↑D8		;BYTE SIZE
	"A" ,,	0		;TRANSFER TYPE (ASCII)
	"F" ,,	0		;STRUCTURE (FILE)
	"S" ,,	0		;MODE (STREAM)

;MISCELLANEOUS
RECPT0:	POINT	7,RECBUF	;POINTER TO FIRST CHAR -1 OF PTY DIALOGUE
				;  RECORDING BUFFER
PTYRSH:				;ADDRESS OF DATA TO REINIT PTY SAVE AREA
	POINT	7,PTYHID	;FIRST-1 CHAR OF BUFFER
	POINT	7,PTYHID
	RECSIZ*5		;# OF BYTES WE CAN STORE
	SUBTTL	OTHER TABLES AND STUFF

;SIGNON STRING

DEFINE XX(V,U,E,W) <
IFE W,<
SRVMSG:	SIXBIT	\%% FTP S&ERVER& V'U(E)#!\
>
IFN W,<
SRVMSG:	SIXBIT	\%% FTP S&ERVER& V'U(E)-W#!\
>>
	VERSTR

;DISPATCH TABLES FOR HOST-DEPENDENT HANDLING

MailCm:	MailCommand		;[96bit] monitor command for mailing

repeat 0,<	;[96bit] forget the tables
HSTTAB:				;HOST NUMBER IN LH, FREE ACCOUNT STRING IN RH

FREACT:	↑D9	,, [SIXBIT\62,"#!\]
	↑D14	,, [SIXBIT\N900AR00!\]
	↑D78	,, [SIXBIT\N900AR00!\]
	↑D142	,, [SIXBIT\N900AR00!\]

	NHOSTS==.-HSTTAB	;NUMBER OF HOSTS IN TABLE

PPNCHG:	0			;PPN TO CHANGE TO WHEN DOING FREE LOGIN
	33125	,, 13750	; N900AR00 (CMUPPN)
	33125	,, 13750
	33125	,, 13750

BYEDSP:	0	,, KJOB.B	;RH IS DISPATCH FOR BYE HANDLING
	0	,, KJOB.F
	0	,, KJOB.F
	0	,, KJOB.F
>
	SUBTTL	LOW SEGMENT
	RELOC	0

ZEROL:		;BEGINNING OF AREA TO ZERO DURING INITIALIZATION

PDL:	BLOCK	PDLSIZ		;STACK
PRJPRG:	BLOCK	1		;PPN OF SUBJOB WHILE LOGGED IN
HSTADR:	BLOCK	1		;HOST TO USE IN DATA TRANSFERS
HstTmp:	Block	1		; place to put a potential new host adr.
HsName:	block	1		; pointer to asciz string of host name
RmtSkt:	BLOCK	1		;REMOTE SOCKET FOR data OPERATIONs
LclSkt:	block	1		; our socket number for data connections
SYSNAM:	BLOCK	5		;LOCAL MONITOR NAME GETS PUT HERE
CMDBUF:	BLOCK	CMDLEN/5+1	;INPUT FTP COMMAND BUFFER
CMDPTR:	BLOCK	1		;POINTER INTO CMDBUF
CmdCnt:	block	1		;[CFE] Count of chars in CmdBuf
RNFBUF:	BLOCK	CMDLEN/5+1	;AREA TO SAVE "RNFR" PATHNAME UNTIL "RNTO"
RNFPTR:	BLOCK	1		;POINTER INTO RNFBUF
RnFCnt:	block	1		;[CFE] Count of chars in RnFBuf
CMDNAM:	BLOCK	1		;NAME OF FTP COMMAND BEING EXECUTED
WATCNT:	BLOCK	1		; # SECONDS WAITED FOR USER TO DO SOMETHING
LHOSTP:	BLOCK	.IBSIZ		;LOCAL HOST PARAMETERS
RECBUF:	BLOCK	RECSIZ		;REGION FOR RECORDING PTY DIALOGUE
PTYHID:	BLOCK	RECSIZ		;REGION FOR SAVING PTY OUTPUT

ICPBLK:				;FILE BLOCK FOR DOING ICP
IMPIBL:	BLOCK	FBSIZE		;IMP TELNET INPUT BLOCK
IMPOBL:	BLOCK	FBSIZE		;IMP TELNET OUTPUT BLOCK
PTYIBL:	BLOCK	FBSIZE		;PTY INPUT (SUBJOB OUTPUT) BLOCK
PTYOBL:	BLOCK	FBSIZE		;PTY OUTPUT (SUBJOB INPUT) BLOCK
IMPCBL:	BLOCK	PBSIZE		;FTP COMMAND PSEUDO-FILE BLOCK

ZEREND:		;END OF AREA TO ZERO DURING INITIALIZATION
FILLL:		;BEGINNING OF AREA TO FILL WITH NONZERO DATA

CONBLK:	BLOCK	.IBSIZ		;TELNET CONNECTION BLOCK

BYTSIZ:	BLOCK	1		;DATA CONNECTION BYTE SIZE
XFRTYP:	BLOCK	1		;DATA TRANSFER TYPE
STRTYP:	BLOCK	1		;DATA TRANSFER STRUCTURE
MODTYP:	BLOCK	1		;DATA TRANSFER MODE

RECPTR:	BLOCK	1		;BYTE POINTER FOR RECORDING PTY DIALOGUE
PTYRSL:				;ADDR TO BLT TO TO REINIT PTY SAVE REGION
PTSPNT:	BLOCK	1		;POINTER FOR STUFFING CHARACTERS
PTRPNT:	BLOCK	1		;POINTER FOR PICKING UP CHARACTERS
PTSCNT:	BLOCK	1		;# OF CHARS LEFT TO FILL IN BUFFER
PTYRSE:				;ADDR+1 TO FINISH REINIT

FLLEND:		;END OF AREA TO SETUP DURING INITIALIZATION

	RELOC
	END	FTPSRV